• No results found

Implementation Details of the AIFProver

Averest and the AIFProver share data structures for types, expressions, specifications, and guarded actions as well as available transformations on sets of guarded actions like reduction to Boolean types (which allows the use of SAT solvers). This does not only avoid reimplementations, it also immediately assures that the prover and the synthesis framework will always remain consistent in case the language is extended or changed. For this reason, one has to implement proof goals, theorems and proof rules in the AIFProver. Since Averest has been implemented in Microsoft’s new programming language F#, the AIFProver is also implemented in F#, but due to the capabilities of the .NET framework, any other programming language like C# could have been used as well. The choice of F# was however also made to directly use F# sessions for interactive verification following the spirit of LCF style theorem provers. It is the second version of the AIFProver, which is described here. The previous version was based on the results of [GeSc12a] that focused on the verification of assumption and assertions.

7.1.1 Proof Goals

A proof goal has an identifierid to address different proof goals. Moreover, it contains system defined (sasm) and user defined (uasm) assumptions that can be used for the proof. The EFSM state is encoded by the set of names of the control-flow locations (labels) that hold in that state. Moreover, the set of delayed assignments (prevStep) that were executed in the previous step (and will therefore affect the current state) is stored. The core of the proof goal is an AIF system itself! Note that guarded actions do not only consist of assignments, but also of assumptions, assertions and temporal logic specifications. Thus, the F# type for a proof goal is defined as follows:

type ASM = QName * SpecExpr type proofGoal = {

id : string

sasm : ASM list uasm : ASM list labels : Set<QName> prevStep : GrdAction list system : AIFSystem }

The initial step and all other steps of a program has to be distinguish to deal with the semantics of past temporal operators and the initialization of variables. Hence, the following discriminated union is used to implement it:

type ProofGoal = InitGoal of proofGoal | GenGoal of proofGoal

This allows us to restrict the application of rules for the initial state toInitGoals and all other rules toGenGoals.

7.1 Implementation Details of the AIFProver 141

7.1.2 Theorems

Proved tasks will be stored as theorems, with the type Thm that consists of a similar discriminated union:

type Thm = InitThm of proofGoal | GenThm of proofGoal

The difference of the two kinds of theorems is that InitThm are allowed to use only for InitGoals.

7.1.3 AIF Proof

An entire proof is represented by the data typeaifproof. It contains an unmodified copy of the originally considered AIF file (proofSystem), a list of all still unproved (ProofGoals) and proved (provedTHM) sub-goals as well as a global list of assumptions (proofASM).

type aifproof = {

proofSystem : AIFSystem;

ProofGoals : (ProofGoal * ProofGoal list) list; proofASM : ASM list;

provedTHM : Thm list }

7.1.4 Proof Rules

Proof rules are therefore F# functions that map existing theorems to new theorems. Also, we implement tactics which decompose desired proof goals into a list of subgoals. Starting with a proof goal, a proof tree is generated by applying a tactic to a leaf of the current proof tree. In our implementation, only the leafs of the current proof tree are stored, and if all leafs were finally proven by a decision procedure, the original root becomes a theorem (i.e. a proven proof goal). Proof rules and tactics correspond to each other and are used in forward proofs (where one derives theorems from axioms by rules) and backward proofs (where proof goals are subsequently decomposed into trivial sub-goals), respectively.

The rules listed in Chapter 4 are the basis of the AIFProver and operate at the level of macro steps and have to introduce assumptions and assertions when a proof goal is decomposed into subgoals in order to implement an assume-guarantee deduction system [HeQR00]. For example, consider Figure 7.2: It illustrates the introduction of assumptions and assertions when splitting a sequence P1; P2 into sub-goals. To this end, the corresponding

proof rule is given an intermediate specification ϕ that is added by the rule application as assertion for P1 and as assumption for P2. For this reason, there are assumptions and

assertions that are added by rule applications and others that were part of the original proof goal.

Finally, rules are implemented by the following F# type: type Rule = Thm list -> Thm

142 7 Evaluation P1 assert(x) assume(x) P2 P1 P2

Fig. 7.2: Decomposition of Sequence P1;P2

7.1.5 Proof Management Rules

Besides the described rules for decomposing or proving a (sub-)goal, an interactive verification tool needs to apply the implemented rules, print or skip the current proof goal and rules that make the usability of the system convenient. Additionally, rules for the introduction of new lemmata or the usage of existing lemmata are required.

7.1.6 Tactics

Tactics are a bunch of compiled rules that are used often together. One of the most important tactics is AutoTac, which applies the rules to rewrite immediate assignments, replaces case statements, splits conjunction statements in the conclusion to separate proof goals, shifts implications in the conclusion to the assumptions and tries to prove proof goals before disjunctions are unstitched and a second solver run is made. Each rule is applied to all new generated proof goals until no changes occur and then the next rule is used.

Besides other tactics there are simplification tactics and a tactic that decomposes a proof goal into several goals, depending on a case-statement, which is the usual representation of an equation.

Finally, tactics are implemented by the following F# type: type Tactic = ProofGoal list -> ProofGoal list

7.1.7 Structure of the AIFProver

Figure 7.3 shows the structure of the AIFProver and the embedding in the Averest system. First, a Quartz program is compiled to an AIF file and fed into the AIFProver. The user decides then which rules are applied by inspecting the source code or simulation results coming from the simulator contained in the Averest system. These rules prove a goal or decompose them into several sub-goals.