Tout d'abord, je souhaite remercier Andrei, pour le temps qu'il m'a consacré au jour le jour, en particulier en période de rédaction, pour me conseiller, corriger mes erreurs et relire toutes mes preuves, même les plus mal écrites. J'ai surtout beaucoup apprécié nos discussion, à la fois professionnellement et personnellement. Je souhaite également remercier Claude, pour son aide et ses conseils, ainsi que pour ses encouragements jusqu'à la soutenance. Son tact, son calme et sa vision positive m'ont beaucoup aidée à appréhender sereinement les dernières étapes du parcours. Merci aussi à Sylvain, qui a orienté mes recherches et m'a encadrée durant toute ma première année. Ses conseils techniques m'ont été très utiles tout au long de ma thèse.

Cette thèse ayant été faite en convention CIFRE, j'ai également passé une grande partie de mon temps en entreprise. Je souhaite donc remercier Yannick et Johannes, qui m'ont accompagnée et encouragée pendant ces 3 ans. Ils m'ont laissée libre de choisir l'organisation de mon temps tout en m'intégrant comme membre à part entière de l'équipe. Nous avons eu de nombreuses discussions enrichissantes (techniques ou non), en particulier devant la machine à café.

Merci également à mes rapporteurs, Nikolaj Bjørner et Albert Rubio, et ainsi qu'aux autres membres de mon jury, Stephan Merz et Joffroy Beauquier, pour leurs remarques et leurs conseils qui à la fois m'ont permis d'améliorer la qualité de mon mémoire et m'ont donné de nouvelles pistes pour aller plus loin.

Je tiens aussi à remercier tous ceux avec qui j'ai travaillé et discuté ces trois dernières années, à la fois au laboratoire et en entreprise. Que ce soit d'un coté ou de l'autre, la bonne ambiance m'a toujours aidée à venir travailler, même dans les moments de fatigue ou de baisse de moral. En particulier, je souhaite remercier mes collègues de bureau, Raphaël et Jérôme, et Asma, Catherine et Stéfania pour nos conversations. Une dédicace spéciale à Elie, avec qui j'ai partagé de nombreuses discussions sur les mérites d'une thèse en CIFRE.

Finalement, pour le soutien moral qu'ils m'ont apporté tout au long de ma thèse, je souhaite remercier ma famille, en particulier mes parents, Fred, Camille et François, qui m'ont encouragée pendant la soutenance et aidée pour le pot. Merci aussi à Emmanuel, mon petit ange, qui a égayé la dernière année de ma thèse et m'a aidée à toujours bien séparer vie professionnelle et vie privée. Le mot de la fin est pour Xavier, mon mari, qui m'a toujours soutenue, a gardé Emmanuel quand je faisais des heures sup le soir avec Andrei et a relu maintes fois mes écrits à la recherche des nombreuses fautes d'orthographe. Nos longues discussions m'ont très souvent aidée à faire la part des choses et à continuer à avancer.

Résumé

Les solveurs SMT sont des outils dédiés à la vérifications d'un ensemble de formules mathématiques, en général sans quantificateurs, utilisant un certain nombre de théories prédéfinies, telles que la congruence, l'arithmétique linéaire sur les entiers, les rationnels ou les réels, les tableaux de bits ou les tableaux. Ajouter une nouvelle théorie à un solveur SMT nécessite en général une connaissance assez profonde du fonctionnement interne du solveur, et, de ce fait, ne peut en général être exécutée que par ses développeurs.

Pour de nombreuses théories, il est également possible de fournir une axiomatisation finie en logique du premier ordre. Toutefois, si les solveurs SMT sont généralement complets et efficaces sur des problèmes sans quantificateurs, ils deviennent imprévisibles en logique du premier ordre. Par conséquent, cette approche ne peut pas être utilisée pour fournir une procédure de décision pour ces théories.

Dans cette thèse, nous proposons un cadre d'application permettant de résoudre ce problème en utilisant des déclencheurs. Les déclencheurs sont des annotations permettant de spécifier la forme des termes avec lesquels un quantificateur doit être instancié pour obtenir des instances utiles pour la preuve. Ces annotations sont utilisées par la majorité des solveurs SMT supportant les quantificateurs et font partie du format SMT-LIB v2.

Dans notre cadre d'application, l'utilisateur fournit une axiomatisation en logique du premier ordre de sa théorie, ainsi qu'une démonstration de sa correction, de sa complétude et de sa terminaison, et obtient en retour un solveur correct, complet et qui termine pour sa théorie. Dans cette thèse, nous décrivons comment un solveur SMT peut être étendu à notre cadre nous basant sur l'algorithme DPLL modulo théories, utilisé traditionnellement pour modéliser ls solveurs SMT. Nous prouvons également que notre extension a bien les propriétés attendues.

L'effort à fournir pour implémenter cette extension dans un solveur SMT existant ne doit être effectué qu'une fois et le mécanisme peut ensuite être utilisé sur de multiple théories axiomatisées. De plus, nous pensons que, en général, cette implémentation n'est pas plus compliquée que l'ajout d'une unique théorie au solveur. Nous avons fait ce travail pour le solveur SMT Alt-Ergo, nous en présentons certains détails dans la thèse.

Pour valider l'utilisabilité de notre cadre d'application, nous avons prouvé la complétude et la terminaison de plusieurs axiomatizations, dont une pour les listes impératives doublement chaînée, une pour les ensembles applicatifs et une pour les vecteurs de Ada. Nous avons ensuite utilisé notre implémentation dans Alt-Ergo pour discuter de l'efficacité de notre système dans différents cas.

Generic Decision Procedures for Axiomatic First-Order Theories

Software often replaces or helps humans in critical domains such as transportation, avionics, space, or medical areas. Since its failure may cost a lot, both materially and in terms of human lives, this software is thoroughly verified so that it offers sufficient levels of confidence that it operates correctly. Historically, this verification is mostly done by testing. Still, since testing is expensive, there is a trend to replace or to complement it by methods based on automated mathematical analysis on source code, called formal verification. Ada is a programming language targeted at real-time embedded software which requires a high level of safety, security, and reliability. In particular, it provides a wide range of run-time checks, for example for buffer overflows, and has a verbose syntax that makes it easy to read and debug. For these reasons, Ada is nowadays used in domains where software cannot be allowed to fail.

SPARK [6], co-developed by Altran and AdaCore, is a subset of Ada targeted at formal verification. Its restrictions ensure that the behavior of a SPARK program is unambiguously defined (unlike Ada). It excludes constructions that cannot easily be verified by automatic tools. The SPARK language and toolset for static verification has been applied for many years in onboard aircraft systems, control systems, cryptographic systems, and rail systems [START_REF] O'neill | SPARK -a language and tool-set for high-integrity software development[END_REF].

The Ada 2012 and SPARK 2014 Languages

Ada 2012 [START_REF] Barnes | Rationale for Ada 2012: 1 contracts and aspects[END_REF] is the latest version of the Ada language. It contains new features for specifying the behavior of programs, such as subprogram contracts and type invariants. When given a specific compilation switch, the Ada compiler can turn these constructs into assertions to check at run time. Thanks to this switch, the conformance of the implementation of a program to its specification can be checked dynamically during the process of unit testing.

Complex contracts can be expressed thanks to the new constructs that have been introduced in Ada 2012. They include in particular conditional expressions and universally and existentially quantified expressions over finite ranges of integers.

Here is the specification of an Ada 2012 function, named Max _ Array:

function Max _ Array (A : Elt _ Array; EMin : Element) return Element with Post => Max _ Array'Result >= EMin and (for all J in A'Range => Max _ Array'Result >= A (J)) and (if Max _ Array'Result /= EMin then (for some J in A'Range => Max _ Array'Result = A (J)));

It takes as input an array A and a minimal bound EMin and returns the maximum of EMin and the elements stored in A. It has a postcondition that uses a universally quantified expression, an existentially quantified expression, and a conditional expression. It states that the result of Max _ Array is bigger than both EMin and the elements of A and that, if Max _ Array does not return EMin then it returns an element of A.

Here is a possible implementation of this function. It uses a loop to go through the array A and stores in Result the biggest element encountered so far: Testing is the most common way to verify that a program is safe, that is, that it does not fail at run-time, and that it behaves as is required by its contract. Still, formal verification can also be used to increase confidence in a program, in particular since it gives guarantees for every execution of the program.

The SPARK 2014 language is a subset of Ada 2012, augmented with features specific to formal verification. It comprises most of the Ada 2012 language excluding constructs which are not easily amenable to sound static verification. Features such as pointers, side effects in expressions, aliasing, goto statements, controlled types (e.g. types with finalization) and exception handling are excluded.

The SPARK 2014 language is designed so that both the flow analysis -checking that there is no access to uninitialized variables and that global variables and subprogram parameters are accessed appropriately -and the proof of program -checking the absence of run-time errors and the conformance to the contract -can be checked. It provides dedicated features that are not part of Ada 2012. In particular, contracts can also contain information about data dependencies, information flows, state abstraction, and data and behavior refinement that can be checked by the SPARK 2014 tools. Essential constructs for formal verification such as loop variants and invariants have also been introduced.

Deductive Verification in SPARK 2014

As an alternative to testing, deductive verification, also called proof of programs, can be used to check that a program is run-time error free and that it complies with its specifications. As described in Figure 1.1, the SPARK 2014 tool for proof of program, named GNATprove, uses a specialized intermediate language named WhyML [START_REF] Bobot | Why3: Shepherd your herd of provers[END_REF]. It is an ML-like programming language with support for proof-only features such as abstract types and functions with no definition, non executable contracts and assertions, as well as lemmas and axioms. The Why3 tool can be used to extract logical statements out of WhyML code, whose validity implies the soundness of the WhyML program. It can then be used as a frontend for a large set of both automatic and interactive provers.

SPARK 2014 source code and contracts

Gnat2Why

WhyML files Why3 + Alt-Ergo Proof results We can use GNATprove to verify our implementation of the Max _ Array function. It first checks that there is no flow error in the design of the program, for example, no use of uninitialized variable and no unintended flow of information. Then, it formally verifies both the absence of run-time errors and the validity of assertions and contracts. We see in Figure 1.2 that the postcondition of Max _ Array cannot be verified by GNATprove. Indeed, deductive verification techniques used inside the SPARK 2014 tool sometimes require additional annotations. For the proof of the postcondition of Max _ Array to go through, we need to specify a loop invariant, that is, a property that is true at every iteration of the loop. It allows one to describe how variables

GNATprove

Challenges in Deductive Verification of Programs

As critical software becomes more and more complex, it becomes difficult to gain enough confidence on an industrial software by testing only without prohibitively increasing the development costs. Formal methods are a possible alternative to tests for verification of critical software: instead of executing the source code, they proceed to mathematical analyses that establish guarantees about every possible execution of the software. These techniques have already been used successfully in the industry for example by Airbus [START_REF] Souyris | Formal verification of avionics software products[END_REF] and Siemens [START_REF] Behm | METEOR: A successful application of B in a large project[END_REF]. Thanks to the support of corresponding certification authorities, formal methods, and in particular the B-method, are widely used in the railway domain. As formal methods were recently adopted by avionic certification authorities as an alternative to testing (see , a new market is open for them.

Deductive verification raises several scientific challenges. First, the specification language must be defined so that it is expressive enough to allow the specification of complex programs while remaining amenable to static verification. Ease of use can also be improved by automatically generating annotations, for example loop invariants [START_REF] Bjørner | Automatic generation of invariants and intermediate assertions[END_REF][START_REF] Halbwachs | Discovering properties about arrays in simple programs[END_REF][START_REF] Kovács | Finding loop invariants for programs over arrays using a theorem prover[END_REF][START_REF] Larraz | SMT-based array invariant generation[END_REF][START_REF] Moy | Modular inference of subprogram contracts for safety checking[END_REF]. Then, there is ongoing work on efficiently extracting the logical propositions that express the soundness of the program, in particular in presence of aliasing as it is subject to combinatorial explosion [START_REF] Leino | Efficient weakest preconditions[END_REF]. A survey of what has been done for aliasing can be found for example in either of Asma Tafat's thesis [START_REF] Tafat | Preuves par raffinement de programmes avec pointeurs[END_REF] (in French) or Romain Bardou's thesis [START_REF] Bardou | Verification of Pointer Programs Using Regions and Permissions[END_REF] (in English). Finally, efficient deductive verification requires efficient solvers to prove the validity of the logical propositions extracted from programs. Not all these formulas can be proved automatically as they are usually expressed in a combination of first-order logic and non-trivial theories such as integer arithmetic which is undecidable. It is still worthwhile to improve the solvers so that more can be proved. My thesis works toward this last goal.

Landscape in Deductive Verification of Programs

Popular techniques for formal verification of programs include bounded model checking, abstract interpretation, and deductive verification. The idea behind abstract interpretation [START_REF] Cousot | Abstract interpretation: a unified lattice model for static analysis of programs by construction or approximation of fixpoints[END_REF] is to model every possible execution of a program using abstract domains that only describe the part of program states in which we are interested. The abstract interpreter uses this abstraction to compute an over-approximation of the values that a variable may take at a given program point. This over-approximation can then be used to verify that a safety property holds during every execution of the program. These abstract domains can be more or less precise depending on which property we want to check, precision being at the expense of efficiency and sometimes termination. For example, the values that can be taken by an integer variable can be modeled by a sign, an interval, or more complex structures like convex polyhedron used to model dependencies between several integer variables.

Bounded model checking [START_REF] Clarke | Model checking[END_REF] consists in systematic exploration of all possible executions of a program up to a predefined bound. The program is not executed but its execution is simulated so that the state of the program at each program point is determined for every input within the bounds. The model checker can then verify that a safety property holds for every execution of the program up to the bound. Since it needs to simulate every program execution, model checking techniques may become unpractical for large bounds on its input.

Deductive verification is based on the generation of mathematical formulas, called verification conditions or proof obligations, which express the correctness of the program.

Deductive Verification Tools for Mainstream Languages

There are several tools for deductive verification of C programs. The verifier VCC [START_REF] Cohen | VCC: A practical system for verifying concurrent C[END_REF], developed at Microsoft Research, can be used to prove correctness of a concurrent C program annotated with contracts, such as pre-and postconditions and type invariants. The HAVOC [START_REF] Qadeer | Algorithmic verification of systems software using SMT solvers[END_REF] verifier, also developed at Microsoft Research, is specialized in the verification of systems software. In particular, it provides an accurate memory model for C accounting for low-level operations such as pointer arithmetic, address-of operations, and casts. The Frama-C [START_REF] Cuoq | Frama-C[END_REF] tool-suite, developed jointly by the LSL laboratory at CEA and the Toccata team at Inria Saclay, provides several static analysis tools to verify a C program's correctness. In particular, it includes tools based on abstract interpretation and on deductive verification of C programs annotated using a formal specification language named ACSL. Both VCC and Frama-C are based on classical first-order logic. Verifast [START_REF] Jacobs | The VeriFast program verifier[END_REF] is a verifier for single-threaded and multi-threaded C and Java programs annotated with contracts written in separation logic [64].

Apart from Verifast, there are several other tools for deductive verification of Java code. The Extended Static Checker for Java version 2 (ESC/Java2) [START_REF] Cok | Esc/Java2: Uniting Esc/Java and JML[END_REF], developed at IT University of Copenhagen, applies deductive verification to Java programs annotated with the Java Modeling Language (JML). The KeY system [START_REF] Ahrendt | The KeY system: Integrating object-oriented design and formal methods[END_REF] allows formal verification of Java code specified in dynamic logic. An additional layer allows to alternatively specify programs using JML or the object constraint language that is part of the UML standard. The proof obligations generated for this verification are discharged by a theorem prover for first-order dynamic logic. The KIV tool [START_REF] Balser | Formal system development with KIV[END_REF], developed at University of Ulm, also verifies formal requirements specifications written in a higher-order algebraic specification language. In particular, it supports specifications coming from UML for Java. Deduction is based on a sequent calculus.

The language Spec# [7] is a formal language for contract-based specification of C# programs developed at Microsoft Research. It supports specification of object invariants in multi-threaded programs.

Some programming languages have been designed specifically to facilitate formal verification. For example, Dafny [START_REF] Leino | Dafny: An automatic program verifier for functional correctness[END_REF], developed at Microsoft Research, is an imperative object-based language designed for formal verification. It supports generic classes and dynamic allocation, but it also provides built-in specification constructs. These constructs include pre-and postconditions as well as some additional constructs to facilitate program annotation such as updatable ghost variables that are ignored by the compiler. As another example, F � [START_REF] Swamy | Verifying higher-order programs with the Dijkstra monad[END_REF], is a higher order, effectful programming language based on F � , that was designed with program verification in mind.

Verification Condition Generation

Tools for deductive verification of programs work by transforming program properties into logical formulas called verification conditions or proof obligations. The validity of those logical formulas, which implies the correctness of the program, can then be checked by automatic provers. One of the most common ways to extract verification conditions is the Weakest Precondition (WP) calculus [START_REF] Dijkstra | Guarded commands, nondeterminacy and formal derivation of programs[END_REF]59,[START_REF] Leino | Efficient weakest preconditions[END_REF] .

The WP calculus is based on Hoare logic [START_REF] Hoare | An axiomatic basis for computer programming[END_REF] which associates both a precondition P and a postcondition Q to every statement S of the program. It infers a precondition W P(S, Q) for a statement S of a program, its postcondition Q being given. The WP calculus generates the weakest precondition strong enough to enforce the validity of Q after the execution of S. Verifying a program S with a precondition P and postcondition Q then amounts to verifying that W P(S, Q) is implied by the user-supplied precondition P. Sometimes, it is impossible to compute the weakest precondition for a statement. It is the case in particular for loop statements. In this case, it can be necessary to manually supply additional information to the calculus. Loop invariants are properties that can be used as the postcondition for a loop body. The WP calculus then simply checks that the loop invariant is inductive -if it holds at an iteration then it also holds at the next iteration -and that it is true before the first iteration.

Generating verification conditions for mainstream languages is a complex task that can be alleviated by splitting it into two steps. The program and its annotations are translated into an intermediate language on which the verification conditions are generated. The transformation into the intermediate language is made easier by the fact that there are still primitive program constructs and, since the intermediate language is simpler than a classical language, verification condition generation is facilitated. What is more, the verification condition generator can then easily be shared between different tools.

Boogie [START_REF] Leino | This is Boogie 2[END_REF], developed at Microsoft Research, is a non executable verification language. It mixes imperative components with pure, mathematical ones, such as logical quantification. It is used as a back-end for several verifiers such as Dafny, VCC, HAVOC, and Spec#. Boogie is also the name of the tool that generates verification conditions from Boogie programs. This tool can also infer some loop invariants.

The Why3 platform [START_REF] Bobot | Why3: Shepherd your herd of provers[END_REF], developed in Toccata team, can be used as a front-end for provers and a middle-end for analysis of realistic programming languages. Its language WhyML is used as intermediate language by Frama-C and GNATprove. It is made of two parts. The logical part can be used to express mathematical properties. It includes type polymorphism, recursive algebraic data types, recursive function symbols, inductive predicates, and pattern matching. It is used to express the verification conditions that can be given to the provers. The program part is an ML-like programming language with annotations such as loop-variant and invariant and pre-and postconditions. The tool that generates verification conditions from WhyML programs is also called Why3.

Automatic Theorem Provers and SMT Solvers

Verification conditions coming from program verification can be discharged using either interactive theorem provers, automatic theorem provers, or Satisfiability Modulo Theory (SMT) solvers. Since we are interested in automated program verification, we only consider the latter two.

Automatic theorem provers are able to decide in a semi-complete way the satisfiability of a set of first-order formulas. Vampire [65] is an automatic theorem prover developed at Manchester University. It uses resolution and superposition calculus to decide the satisfiability of firstorder propositional calculus with equality. The E theorem prover [START_REF] Schulz | E -A Brainiac Theorem Prover[END_REF], developed at Technishe Universität München, also decides first-order logic with equality using resolution and superposition calculus. We can also cite iProver [46], based on an instantiation calculus, also developed at Manchester University, and SPASS [START_REF] Weidenbach | Spass version 2.0[END_REF], an automatic theorem prover for first-order logic with equality developed at Max Planck Institute for Computer Science that combines superposition calculus and splitting for explicit case analysis.

SMT solvers [9,[START_REF] De Moura | Satisfiability modulo theories: An appetizer[END_REF] try to decide satisfiability of a set of, usually ground, formulas modulo a background theory. Most classical theories include:

• Equality with Uninterpreted Functions, also known as free functions, which is congruence closure over uninterpreted function symbols,

• Linear Arithmetic, that is, arithmetic with only +, -, and multiplication with a constant, over integers, rationals, or reals,

• Difference Arithmetic, where predicates must be of the form xy ≤ c,

• Non-Linear Arithmetics, with both + and ×,

• Bit-Vectors, that is, arithmetics over machine integers, and

• Arrays, that are functional maps with an access function get and an update function set.

Solving an SMT problem amounts to combining an algorithm for resolution of satisfiability over propositional logic (SAT) with solvers for the various background theories. Most SMT solvers are based on the Davis-Putnam-Logemann-Loveland (DPLL) algorithm for solving the SAT problem [START_REF] Davis | A machine program for theorem-proving[END_REF].

This algorithm works on a set of formulas including only logical variables combined using conjunction, disjunction, and negation operators. It tries to assign each logical variable to either true or false in a way that sets every formula in the input set to true. If there is such a truth assignment, the input set of formulas is said to be satisfiable. For example, the set of formulas {a 1 ∨ a 2 , ¬a 1 ∨ a 2 } is satisfiable as it is true whenever a 2 is assigned to true. To determine that a formula is valid, that is, it is true for every value of its logical variables, a SAT solver checks that the negation of the formula is unsatisfiable. For example, ¬(¬a 1 ∨ a 2 ) ∨ ¬a 1 ∨ a 2 is valid as there is no truth values of a 1 and a 2 that satisfy its negation (¬a 1 ∨ a 2 ) ∧ a 1 ∧ ¬a 2 .

The search for the appropriate truth values of the logical variables is done by systematic exploration. The algorithm chooses arbitrarily a truth value for a logical variable. Then it propagates this information in the propositional formulas from the input problem. If it finds a formula that is set to false by the chosen truth values of logical variables, it undoes the last choice and starts over. There are several improvements [START_REF] Nieuwenhuis | Solving SAT and SAT modulo theories: From an abstract Davis-Putnam-Logemann-Loveland procedure to DPLL(T)[END_REF] to this naive search strategy. For example, when a conflict is found, it can be analyzed to determine which decisions were responsible for it so that the algorithm can go back at once to the last decision that is involved in the conflict.

Most SMT solvers provide multiple built-in background theories and it is often the case that the problem we want to solve includes elements coming from several of them. The solvers for these theories need therefore to be combined, and so, without losing termination nor completeness of procedures that decide satisfiability in these theories. There are several methods that can be used to combine theories. The most common one is the Nelson-Oppen combination method [60]. For example, assume we want to decide the satisfiability of the set of formulas L = { f (a) � ≈ f (b + c), b ≈ a -c} modulo the theories of linear integer arithmetics and uninterpreted functions. The idea is to separate the problem into smaller problems containing only function symbols coming from one of the theories. If one of the smaller problems is unsatisfiable then the original problem is unsatisfiable. Otherwise, the solvers for the smaller problems must find an agreement on a set of equivalence classes for the variables that are shared among theories. If there is such a set of equivalence classes then the problem is satisfiable, otherwise, it is unsatisfiable. On our example, we split L into two sets L 1 = { f (a) � ≈ f (d)} and L 2 = {d ≈ b + c, b ≈ a -c}. Both L 1 and L 2 are satisfiable. As a consequence, the two solvers must agree on a set of equivalence classes for the set {a, d}. This is impossible. Indeed, if we choose a ≈ d then L 1 is unsatisfiable in the theory of uninterpreted functions and if we choose a � ≈ d then L 2 is unsatisfiable in the theory of linear integer arithmetics. As a consequence, L is unsatisfiable in the combination of uninterpreted functions and linear integer arithmetics.

The SMT solver Z3 [START_REF] De Moura | Z3: An efficient SMT solver[END_REF], developed at Microsoft Research, is the default solver used by Boogie. It includes, among others, theories for linear real and integer arithmetic, fixed-sized bit-vectors, uninterpreted functions, and extensional arrays. It supports quantifiers and model generation. The open-source SMT solver CVC4 [8], the successor of CVC3 [START_REF] Ge | Solving quantified verification conditions using satisfiability modulo theories[END_REF], is a joint project of New York University and University of Iowa. It includes theories for rational and integer linear arithmetic, arrays, tuples, records, inductive data types, bit-vectors, and uninterpreted functions. Like Z3, it supports quantifiers and model generation. VeriT [16] is an open-source SMT solver developed by University of Nancy, Inria and Federal University of Rio Grande do Norte. It supports uninterpreted functions and difference logic on real numbers and integers. It also includes quantifier reasoning capabilities through the integration of a first-order prover.

The verification conditions generated from SPARK 2014 programs are discharged by an open-source SMT solver named Alt-Ergo [START_REF] Bobot | Implementing polymorphism in SMT solvers[END_REF]. Alt-Ergo, developed in Toccata team, is dedicated to the proof of theorems coming from the verification of programs. It provides a built-in support for linear arithmetic over integers and rationals, non-linear arithmetic, functional arrays, enumerated data-types, record data-types, associative and commutative symbols, and fixed-size bit-vectors. It also supports first-order logic which is essential for program verification, as well as type polymorphism. In Alt-Ergo, theory combination is not done using the Nelson-Oppen method but rather using a variant of Shostak combination mechanism [START_REF] Cyrluk | On Shostak's decision procedure for combinations of theories[END_REF][START_REF] Shostak | Deciding combinations of theories[END_REF] called CC(X) [24].

Problem and Contributions 1.3.1 First Order Axiomatizations as Decision Procedures

Verification conditions generated in program verification use a number of theories. Some of them are supported specifically by the prover used, we call them background theories of the prover. SMT solvers usually decide in a terminating and complete way the satisfiability of quantifier-free formulas in the theories they support. We say that they are decision procedures for the satisfiability of such formulas. Of course, not every useful theory is supported by every SMT solver and many theories can be designed that are not supported by any solver. Adding a background theory to an SMT solver is a complex and time-consuming task that requires internal knowledge of the solver and often access to its source code.

For many useful theories, one can alternatively provide a first-order axiomatization to the SMT solver, provided it handles quantifiers. To give some examples, Simplify [START_REF] Detlefs | Simplify: a theorem prover for program checking[END_REF], CVC3, CVC4, Z3, and Alt-Ergo support first-order logic. Since it is undecidable, any automated prover is at best semi-complete on first-order problems and even semi-completeness is unattainable when non-trivial background theories, like integer arithmetic, are involved. To improve the chance of finding a proof, most SMT solvers give the user some control over instantiation of quantified formulas, by allowing to annotate quantifiers with so-called instantiation patterns also known as triggers.

The basic idea behind triggers is that the solver maintains a set of "known" terms (which usually are simply the terms occurring in assumed facts) and for instantiation to take place, a known term must match the pattern. Pattern matching modulo equality was introduced in the Stanford Pascal Verifier [58] and is now used in most SMT solvers supporting quantifiers. It has been demonstrated that by careful restriction of instance generation in a first-order theoryin a way that can be expressed via instantiation patterns-one can both preserve completeness and ensure termination, thus obtaining a decision procedure for the theory. The most prominent example is the decision procedure for the theory of functional arrays by Greg Nelson [58], which we will consider in greater detail below. More recently, the same work has been done for specification of more complex data-structures [START_REF] Mcpeak | Data structure specifications via local equality axioms[END_REF][START_REF] Chatterjee | A low-level memory model and an accompanying reachability predicate[END_REF].

Example 1.1.
Here is an axiomatization for the theory of non-extensional arrays as defined by Greg Nelson. This axiomatization uses two function symbols, one, named get, to model access in an array and another, named set, to model update of an array. It contains two axioms that describe how an array is modified by an update. The first one states that an access to the updated index returns the updated element and the second one, given with two different triggers, states that an access to any other index returns the element that was previously stored at this index.

W array =    ∀a, i, e.
[set(a, i, e)] (get(set(a, i, e), i) ≈ e) ∀a, i, j, e.[get(set(a, i, e), j)] (i � ≈ j → get(set(a, i, e), j) ≈ get(a, j)) ∀a, i, j, e.[set(a, i, e), get(a, j)] (i � ≈ j → get(set(a, i, e), j) ≈ get(a, j))

 



The trigger of the first axiom expresses that it need only be instantiated with three terms a, i, and e if the term set(a, i, e) appears in the problem. For the second axiom, there are two different cases where it should be instantiated: if the term get(set(a, i, e), j) appears in the problem or if both set(a, i, e) and get(a, j) appear in the problem. These two cases allow the equality get(set(a, i, e), j) ≈ get(a, j) to be rewritten both ways. Unfortunately, the user cannot hope to prove that a given first-order SMT solver is complete and terminating on a particular set of axioms with triggers for her theory of interest. Triggers are not and were never meant to change the satisfiability of a first-order formula. Instantiation patterns are rather considered as hints to what instances are more likely to be useful, and an SMT solver can base its decisions on the triggers given by the user as well as on the triggers that it infers itself using some heuristic. In pursuit of completeness, a solver has the right to use any instantiation strategy it deems useful, and it may even ignore the triggers altogether.

And yet if we want our axiomatization to give us a decision procedure, we must be able to control instantiation of axioms in a precise and reliable manner.

Overview of the Contributions

We propose in Chapter 2 a framework to add a new background theory to an SMT solver by providing a first-order axiomatization with triggers. In order to restrict instantiation in a deterministic way, we give a formal semantics to formulas with triggers, which promotes triggers to the status of guards, forbidding all instances but the ones described by the pattern. Using this semantics, we define, independently from a specific solver's implementation but modulo its background theory, three properties of a set of first-order axioms with triggers-namely, soundness, completeness, and termination-that are required for a solver to behave as a decision procedure for this axiomatization. We give a fairly exhaustive axiomatization for imperative doubly-linked lists as an example and we provide completeness and termination proofs of this axiomatization in our framework.

In Chapter 3 we describe how a solver for first-order formulas with triggers can be built on top of a ground SMT solver. We show that, when given an axiomatization that meets the three conditions of soundness, completeness, and termination, such a solver yields a decision procedure for this axiomatization. Although good in theory, in practice, we show on some simple examples that this black-box extension mechanism results in poor performances and therefore does not yet give a practical way for adding a new theory to an SMT solver, calling for a better integrated approach.

We consider in Chapter 4 the well-known Abstract DPLL Modulo Theory framework [START_REF] Nieuwenhuis | Solving SAT and SAT modulo theories: From an abstract Davis-Putnam-Logemann-Loveland procedure to DPLL(T)[END_REF], a standard theoretic model of modern SMT solvers. We describe a variation of this framework that handles first-order formulas with triggers. We show that for any axiomatization that meets the three conditions of soundness, completeness, and termination, a compliant SMT solver behaves as a decision procedure for this axiomatization.

More precisely, consider an SMT solver which effectively decides quantifier-free problems in some background theory T . In the simplest case, T can be the theory of equality and uninterpreted function symbols (EUF). It can also be the theory of linear arithmetic, bit vectors, associative arrays, or any combination of the above. A user of that prover wants to extend T with some new theory-for example, that of mutable container data structures-and obtain a decision procedure for the ground problems in this extended theory which we denote T � . To this purpose, the user writes down a set of first-order axioms with triggers and proves that this axiomatization is a sound, complete, and terminating representation of T � in T . Since the three conditions are formulated in purely logical terms, no specific knowledge of inner prover mechanisms is required to do that proof. Now, provided that the solver implements our extension of DPLL(T )-or any other method that treats axioms with triggers in accordance with our semantics-the solver is guaranteed to decide any quantifier-free problem in T � in a finite amount of time.

The method is not intended to extend ground SMT solvers to first-order logic. Neither do we strive to give some ultimate semantics for triggers, on which all first-order SMT solvers should converge. Our restrictive and rigorous treatment of quantifiers and triggers should be only applied to the axioms of the theory we wish to decide, and not to first-order formulas coming with a particular problem. Indeed, while we must restrict instantiation in the former case to guarantee termination, we would gain nothing by applying the same restrictions to ordinary firstorder formulas. On the contrary, we are likely to prevent the solver from finding proofs which otherwise would be discovered, and, moreover, the additional checks needed to implement the restrictions will hinder the solver's performance.

We have implemented our extension of DPLL(T ) in the first-order SMT solver Alt-Ergo. To validate our approach, we compare, on several case studies, the efficiency of a first-order axiomatization with and without hand-written triggers, using the built-in quantifier handling in SMT solvers. In Chapter 5, we describe a theory for mathematical sets. We then use it for the verification of mathematical formulas coming from the development of programs using the B method [START_REF] Schneider | The B-method: An introduction[END_REF] through Atelier B. In Chapter 6, we describe a theory that models some aspects of vectors in SPARK 2014. We design an axiomatization for this theory and prove that it is sound, In this chapter, we introduce a formalization for a first-order logic with a notation for triggers that restrict instantiation. In this logic, we then define properties -soundness, completeness, and termination -that a set of formulas with triggers should guarantee in order to provide a decision procedure for a theory T � that extends a background theory T . We assess in Section 2.2 the usability of these definitions on an axiomatization for imperative doubly-linked lists. We show that this axiomatization is sound, complete, and terminating following our definitions. Finally, in Section 2.3, we give some advices for designing a complete and terminating axiomatization.

Formalization

In first-order SMT solvers, triggers are used to favor instantiation of universally quantified formulas with "known" terms that have a given form. Intuitively, a term is said to be known when it appears in a ground fact assumed by the solver. Here is an example of a formula with a trigger in SMT-LIB version 2 [10] notation:

(forall ((x Int)) (! (= (f x) c) :pattern ((g x))))
The bang symbol under the universal quantifier marks an annotated sub-formula and the trigger (g x) appears after the keyword :pattern. The commonly agreed meaning of the above formula is:

(= (f t) c
) holds for all terms t of type Int such that (g t) is known.

The concept of triggers can be extended to literals. If an axiom can only deduce new facts when instantiated with terms having a given property P, it may be unnecessary to instantiate it with a term t without knowing a priori that P(t) is true. In other words, we can restrict instantiation not just by the shape of known terms but also by what is known about them. For example, in the theory of extensional arrays, it is enough to apply the extensionality axiom on arrays that are known to be different [START_REF] Goel | Deciding array formulas with frugal axiom instantiation[END_REF]:

∀a 1 , a 2 : array.[a 1 � ≈ a 2 ] (a 1 � ≈ a 2 → (∃i : index. get(a 1 , i) � ≈ get(a 2 , i))
In this section, we extend the standard first-order logic with constructions for triggers. For the sake of simplicity, our formalization is unsorted even if all our examples use sorts. We define what it means for a formula with triggers to be true in the context of a given set of known facts and terms. Finally, we introduce the properties of soundness, completeness, and termination for sets of first-order formulas with triggers.

Preliminary Notions

We work in classical untyped first-order logic and assume the standard notation for first-order formulas and terms. We denote formulas with letters ϕ and ψ, literals with l, terms with s and t, and substitutions with σ and µ. Other notational conventions will be introduced in the course of the text.

To simplify our definitions, we work on formulas in negative normal form. The syntax of formulas and literals can be described as follows, A being an atom:

ϕ ::= l | ϕ 1 ∨ ϕ 2 | ϕ 1 ∧ ϕ 2 | ∀x.ϕ | ∃x.ϕ l ::= A | ¬A
The transformation into negative normal form uses the following, usual, definitions:

¬(ϕ 1 ∨ ϕ 2 ) =⇒ ¬ϕ 1 ∧ ¬ϕ 2 ¬(ϕ 1 ∧ ϕ 2 ) =⇒ ¬ϕ 1 ∨ ¬ϕ 2 ¬(∀x.ϕ) =⇒ ∃x.¬ϕ ¬(∃x.ϕ) =⇒ ∀x.¬ϕ
We say that a formula is closed if it has no free variables, and that a term, literal, or formula is ground if it has no free variables and no quantifiers. We use T (t), T (l), T (S) to denote the set of all terms that occur in, respectively, a term t, a literal l, or a set of terms or literals S.

We reason modulo some background theory T , which we assume to be fixed for the rest of this section. In the simplest case, T can be the theory of Equality With Uninterpreted Functions (EUF). We assume that the signature of T contains at least one constant symbol to allow constructing the Herbrand universe and can be extended at will with uninterpreted function symbols to allow Skolemization. We use the following definition for atoms, were ≈ is the symbol for equality:

A ::= � | t 1 ≈ t 2 | . . .
The dots stand for other forms of predicates specific to background theories, e.g. comparison for linear arithmetic.

We use Herbrand models in our formalization, that is, we call model a set of literals L containing every valid literal l. Note that a first-order formula is satisfiable if and only if it has an Herbrand model.

We use the standard notation L � ϕ to state that a closed first-order formula ϕ is valid in a Herbrand model L. Let T be a theory, that is, a possibly infinite set of closed first-order formulas. Definition 2.1 (T -satisfiability). We say that a first-order formula ϕ is valid in a model L modulo T , written L � T ϕ if ϕ is valid in L and L is also a model of T . If a first-order formula ϕ has a model modulo T then we say that it is T -satisfiable or, equivalently, that is is satisfiable modulo T .

We sometimes use clauses, that are disjunctive sets of literals. We say that a clause is a unit clause, if it contains only one literal. The empty clause is assumed to be equivalent to false, that is, ¬�.

Logic with Triggers (Syntax and Semantics)

We introduce two new kinds of formulas. A formula ϕ under a trigger l is written [l]ϕ. It can read as if the literal l is true and all its sub-terms are known then assume ϕ. A dual construct for [l]ϕ, which we call a witness, is written �l�ϕ. It can be read as assume that the literal l is true and all its sub-terms are known and assume ϕ. Notice that neither triggers nor witnesses are required to be tied to a quantifier. The extended syntax of formulas can be summarized as follows:

ϕ ::= l | ϕ 1 ∨ ϕ 2 | ϕ 1 ∧ ϕ 2 | ∀x.ϕ | ∃x.ϕ | [l]ϕ | �l�ϕ
On the new constructs, transformation into negative normal form is done using the additional equivalences ¬�l�ϕ =⇒ [l]¬ϕ and ¬[l]ϕ =⇒ �l�¬ϕ.

We write

[t]ϕ for [t ≈ t]ϕ, �t�ϕ for �t ≈ t�ϕ, ⊥ for ¬�, t 1 � ≈ t 2 for ¬(t 1 ≈ t 2 ), ϕ 1 → ϕ 2 for ¬ϕ 1 ∨ ϕ 2 , and ϕ 1 ↔ ϕ 2 for (ϕ 1 → ϕ 2 ) ∧ (ϕ 2 → ϕ 1 )
. If there are several triggers or witnesses in a row, we write [l 1 , . . . , l n ]ϕ for [l 1 ] . . . [l n ]ϕ and �l 1 , . . . , l n �ϕ for �l 1 � . . . �l n �ϕ.

A first-order formula with triggers must be evaluated in the context of a particular set of assumed facts and known terms: Definition 2.2 (World modulo T ). We call world a T -satisfiable set of ground literals. A world L is inhabited if there is at least one term occurring in it, i.e. T (L) is non-empty. A world L is complete if for any ground literal l in the signature of T , either l ∈ L or ¬l ∈ L. Definition 2.3 (Known term modulo T ). A term t is known in a world L if and only if there is a term

t � ∈ T (L) such that L � T t ≈ t �
The key intuition about worlds is that a ground literal l can only be evaluated in a world L if every term t in T (l) is known in the world. If, on the contrary, some term occurring in l is unknown in L, we "refuse" to evaluate the literal, that is neither l nor ¬l is true in L. To express this constraint easily, we use a unary predicate symbol known which we assume to be new and not to appear anywhere else in the problem. Using this symbol, the fact that a term t is known in L, can be equivalently stated as L ∪ � s∈T (L) known(s) � T known(t). We abbreviate the conjunction � t∈S known(t) as known(S), where S is any set of ground terms. Definition 2.4 (Truth value modulo T ). Given a world L and a closed formula ϕ, we define what it means for ϕ to be true in L, written L � T ϕ, by induction on ϕ as follows:

L � T l L � T l and L ∪ known(T (L)) � T known(T (l)) L � T ϕ 1 ∨ ϕ 2 L � T ϕ 1 or L � T ϕ 2 L � T ϕ 1 ∧ ϕ 2 L � T ϕ 1 and L � T ϕ 2 L � T ∀x.ϕ for every term t in T (L), L � T ϕ[x ← t] L � T ∃x.ϕ there is a term t in T (L) such that L � T ϕ[x ← t] L � T [l]ϕ if L � T l then L � T ϕ L � T �l�ϕ L � T l and L � T ϕ
We say that a closed formula ϕ is false in L whenever L � T ¬ϕ. We call ϕ feasible if there exists a world in which ϕ is true.

As we have noted, a formula that contains a term unknown in a world may be neither true nor false in that world. On the other hand, it is impossible for a formula to be both true and false in the same world. In other words, there is no closed formula ϕ and world L such that L � T ϕ and L � T ¬ϕ. This is easily proved by induction on the structure of ϕ.

According to the rules, a formula with a witness �l�ϕ is handled just as the conjunction l ∧ ϕ. Yet a formula with a trigger [l]ϕ is not the same as the disjunction ¬l ∨ ϕ. Indeed, consider a literal l that contains a term unknown in L, so that neither l nor ¬l is true in L. Then we have L � T [l]⊥ but not L � T ¬l ∨ ⊥. However, if L is a complete world, then any ground literal is either true or false in L, and we can replace all triggers with implications. Definition 2.5 (Model modulo T ). A world L is said to be a model of a closed formula ϕ whenever L is complete and L � T ϕ. We call ϕ satisfiable if it has a model.

Relation with Traditional First-Order Logic

Let ϕ be a closed formula and ϕ � be ϕ where all triggers are replaced with implications and all witnesses with conjunctions. As noted above, in any complete world L, L � T ϕ if and only if L � T ϕ � . Moreover, L � T ϕ � if and only if L � T ϕ � . Indeed, since every ground term is known in a complete world, the truth value of quantified formulas and ground literals in our logic coincides with that in the usual first-order logic. Thus, L is a model of ϕ if and only if it is a Herbrand model of ϕ � in T . Consequently, ϕ is satisfiable in the sense of Definition 2.5 if and only if ϕ � is T -satisfiable, which justifies our reuse of the term.

For ground literals or conjunctions thereof, the properties of feasibility and satisfiability are equivalent. A non-literal formula, however, can be true in some world yet have no model. For example, the formula [a] a � ≈ a (which is an abbreviation for [a ≈ a] a � ≈ a) is true in any world where a is unknown, but is false in any complete world.

Feasibility does not imply the existence of a model even in the case where the formula in question contains no triggers or witnesses. Assume T to be the theory of linear arithmetic. Then the formula ∃y.∀x.x ≤ y is true in the world {0 ≤ 0}. Indeed, this world "knows" only one distinct term modulo T and there is no possible instantiation to refute ∀x.x ≤ 0. Of course, the formula ∃y.∀x.x ≤ y has no model, since the only complete world for T is, by definition, the set of all ground literal facts of linear arithmetic.

It is thus all the more remarkable that the following implication holds in the background theory EUF (Equality with Uninterpreted Functions): Theorem 2.1. Let ϕ be a closed first-order formula without triggers and witnesses. Let L be an inhabited world such that L � EUF ϕ. Then ϕ is satisfiable in first-order logic with equality (and therefore has a model in the sense of Definition 2.5).

Proof. We start with some preliminaries. From now on and until the end of this section, we write � for � EUF and � for � EUF . We define an encoding � � that explicitly restricts instantiation of first-order formulas in negative normal form to known terms.

�ϕ 1 ∧ ϕ 2 � � �ϕ 1 � ∧ �ϕ 2 � �ϕ 1 ∨ ϕ 2 � � �ϕ 1 � ∨ �ϕ 2 � �∀x.ϕ� � ∀x.known(x) → �ϕ� �∃x.ϕ� � ∃x.known(x) ∧ �ϕ� �l� � known(T (l)) ∧ l Lemma 2.1.
Let ϕ be a closed first-order formula without triggers and witnesses. If there is an inhabited world L such that L � ϕ, then there is a Herbrand model L � of �ϕ� such that, for at least one ground term ω, L � � known(ω).

Proof. Let us consider the following set of literals L � :

L ∪ known(T (L)) ∪ {¬known(t) | L ∪ known(T (L)) � known(t)}
Since L is inhabited, for at least one ground term ω, L � � known(ω). We show that the set of literals L � is satisfiable. By contradiction, assume L � is unsatisfiable. This can only be the case if there is a set of equalities

t 1 ≈ t � 1 . . . with t i ∈ T (L) and t � i ∈ {¬known(t) | L ∪ known(T (L)) � known(t)} such that L � t 1 ≈ t �
1 ∨ . . . . In the theory EUF, if a set of literals implies a disjunction of equalities then it implies one of them. As a consequence, there must be an equality t ≈ t � such that t ∈ T (L) and t � ∈ {¬known(t) | L ∪ known(T (L)) � known(t)}, which is impossible.

We show by structural induction on ϕ that, for every formula ϕ such that L � ϕ, we have L � � �ϕ�.

• l: We have L � l and, by construction of L � , L � � l.

• ϕ 1 ∨ ϕ 2 : Either L � ϕ 1 or L � ϕ 2 . By induction hypothesis, L � � �ϕ 1 � or L � � �ϕ 2 �. Thus, L � � �ϕ 1 � ∨ �ϕ 2 � = �ϕ 1 ∨ ϕ 2 �. • ϕ 1 ∧ ϕ 2 : Both L � ϕ 1 and L � ϕ 2 . By induction hypothesis, L � � �ϕ 1 � and L � � �ϕ 2 �. Thus, L � � �ϕ 1 � ∧ �ϕ 2 � = �ϕ 1 ∧ ϕ 2 �. • ∀x.ϕ: Let t be a term. If L ∪ known(T (L)) � known(t) then there is t � ∈ T (L) such that L � t ≈ t � . By definition of �, we have L � ϕ[x ← t � ] and hence L � � �ϕ[x ← t � ]� by induction hypothesis. Therefore L � � ∀x.known(x) → �ϕ�. • ∃x.ϕ: There is a term t in T (L) such that L � ϕ[x ← t]. By construction of L � , L � � known(t). Furthermore, by induction hypothesis, L � � �ϕ[x ← t]�. As a consequence, L � � ∃x.known(x) ∧ �ϕ�.
Using Lemma 2.1, we can reformulate our theorem:

Lemma 2.2. Let ϕ be a closed first-order formula without triggers and witnesses. Let L be a Herbrand model of �ϕ� such that at least for one ground term ω, L � known(ω). Then ϕ is EUF-satisfiable.

We can assume that for every non-constant term t, L contains an equality t ≈ c, where c is a constant (such equalities can always be added to a model if needed). We can also assume that ω is a constant.

We define a set of literals L 1 = {l | known does not occur in l and L � �l�} and another

L 2 = L 1 ∪ {t ≈ ω | L 1 ∪ known(T (L 1
)) � known(t) and, for every proper subterm t � of t, L 1 ∪ known(T (L 1 )) � known(t � )}. We show that: (i) L 2 is satisfiable, (ii) for every ground term t, there is t � ∈ T (L 1 ) such that L 2 � t ≈ t � , and

(iii) L 2 � ϕ.
Proof of (i): Since L is satisfiable, so is L 1 . In EUF, for every set of literals L and every pair of terms t 1 and t 2 , if L � t 1 � ≈ t 2 then L ∪ known(T (L)) � known(t 1 ) ∧ known(t 2 ). Indeed, if t 1 is not known in L modulo EUF then we can choose any value for it and, in particular, the value associated to t 2 . Thus, adding an equality between a known term and an unknown term to a set of literals cannot lead to inconsistency. Let t 1 and t 2 be two terms such that L 1 � t 1 ≈ t 2 , L 1 ∪ known(T (L 1 )) � known(t i ), and, for every proper subterm t � of t 1 or t 2 , L 1 ∪ known(T (L 1 )) � known(t � ). Since t 1 cannot be equal modulo L 1 to a subterm of t 2 , we have that L 1 ∪ {t 1 ≈ ω} ∪ known(T (L 1 )) ∪ known(t 1 ) � known(t 2 ). Thus, no matter the order in which the equalities are added to L 1 , they always involve a previously unknown term. As a consequence, L 2 is satisfiable.

Proof of (ii): We show that, for every ground term t, there is t � ∈ T (L 1 ) such that L 2 � t ≈ t � by structural induction over t. We write t as f (t 1 . . .t n ) where n can be zero for constants. By induction hypothesis, for every i in 1..n, there is

t � i ∈ T (L 1 ) such that L 2 � t i ≈ t � i . If L 1 ∪ known(T (L 1 )) � known(t), there is t � ∈ T (L 1 ) such that L 1 � t ≈ t � . Otherwise, con- sider f (t � 1 . . .t � n ). By construction, L 2 � f (t � 1 . . .t � n ) ≈ t. If there is t � ∈ T (L 1 ) such that L 1 � f (t � 1 . . .t � n ) ≈ t � , then the proof is over. Otherwise, L 1 ∪ known(T (L 1 )) � known( f (t � 1 . . .t � n )) and, for every i in 1..n, L 1 ∪ known(T (L 1 )) � known(t � i ). By construction of L 2 , f (t � 1 . . .t � n ) ≈ ω ∈ L 2 . Since L 2 � f (t � 1 . . .t � n ) ≈ t, we have L 2 � t ≈ ω which concludes the proof.
Proof of (iii): We show that, for every ground formula ψ without triggers and witnesses such that L � �ψ�, L 2 � ψ by structural induction over ψ.

• L � �l�. By definition of L 1 , l ∈ L 1 and L 2 � l.

• L � �ψ 1 ∧ ψ 2 � = �ψ 1 � ∧ �ψ 2 �. By induction hypothesis, L 2 � ψ 1 ∧ ψ 2 . • L � �ψ 1 ∨ ψ 2 � = �ψ 1 � ∨ �ψ 2 �. Since L is a model, L � �ψ 1 � or L � �ψ 2 �. By induction hypothesis, L 2 � ψ 1 or L 2 � ψ 2 . Thus L 2 � ψ 1 ∨ ψ 2 .
• L � �∀x.ψ� = ∀x.known(x) → �ψ�. Let t be a ground term. By (ii), there is

t � ∈ T (L 1 ) such that L 2 � t ≈ t � . By construction of L 1 , L � known(T (t � )). We then have L � �ψ� [x ← t � ] and, by immediate induction over ψ, L � �ψ[x ← t � ]�. By induction hypothesis, we have L 2 � ψ[x ← t � ]. Therefore, L 2 � ψ[x ← t] and L 2 � ∀x.ψ. • L � �∃x.ψ� = ∃x.known(x) ∧ �ψ�. Since every ground term is equal to a constant in L, there is a constant c ∈ T (L) such that L � known(c) and L � �ψ� [x ← c]. By immediate induction over ψ, L � �ψ[x ← c]�. By induction hypothesis, L 2 � ψ[x ← c] and L 2 � ∃x.ψ.

Soundness and Completeness

Whenever a user wants to extend the solver's background theory T and provides for that purpose a set of axioms with triggers, she must prove that this axiomatization is an adequate representation of the extended theory T � modulo T .

Definition 2.6 (Soundness modulo T ). An axiomatization W is sound with respect to

T � if, for every T � -satisfiable set of ground literals L, W ∪ L is T -satisfiable. Definition 2.7 (Completeness modulo T ). An axiomatization W is complete with respect to T � if, for every set of ground literals L such that W ∪ L is feasible, L is T � -satisfiable.
Remark 2.1. Note that the two definitions of soundness and completeness are not symmetrical. Indeed, we want a solver to be allowed to stop whenever it has found a world L in which the axiomatization is true while returning satisfiable. In particular, we want the solver to instantiate universal quantifiers using only terms of L and to ignore formulas protected by a trigger l if l is not true in L.

Quite often, T � is the theory defined by the same set of axioms W where all triggers and witnesses are erased. More precisely, we start with a usual first-order axiomatization of the theory of interest, and then annotate axioms with triggers and witnesses in order to restrict instantiation and guarantee the termination of proof search. In this case, to prove soundness, we must show that the added witnesses do not allow us to deduce statements beyond the initial set of first-order axioms. As for completeness, we must show that the added triggers and the restricted semantics of quantifiers do not prevent us from proving every ground statement deducible in the initial axiomatization.

Example 2.1. The proof that the set of axioms W array shown in Example 1.1 is complete modulo EUF closely resembles the proof by Greg Nelson in [58]. We do not give this proof here but show that simpler or more "intuitive" variants of that axiomatization are incomplete.

• Let W 1 array be W array where the trigger in the first axiom is replaced by get(set(a, i, e), i). Consider the set of literals L 1 = {set(a, i, e 1 ) ≈ set(a, i, e 2 ), e 1 � ≈ e 2 }. It is unsatisfiable in the theory of arrays since we have both get(set(a, i, e 1 ), i) ≈ e 1 and get(set(a, i, e 2 ), i) ≈ e 2 . Still, W 1 array ∪ L 1 is true in the world L 1 , since we have no term in L 1 to match the trigger.

• Let W 2 array be W array without the second axiom. Consider the set of ground literals L 2 = {get(set(a, i 1 , e), j) � ≈ get(set(a, i 2 , e), j), i 1 � ≈ j, i 2 � ≈ j}. It is unsatisfiable in the theory of arrays since get(set(a, i 1 , e), j) ≈ get(a, j) and get(set(a, i 2 , e), j) ≈ get(a, j). Yet, W 2 array ∪ L 2 is true in the world L 2 ∪ {get(set(a, i 1 , e), i 1 ) ≈ e, get(set(a, i 2 , e), i 2 ) ≈ e}.

• Let W 3 array be W array without the third axiom. Consider the set of ground literals L 3 = {set(a 1 , i, e) ≈ set(a 2 , i, e), i � ≈ j, get(a 1 , j) � ≈ get(a 2 , j)}. It is unsatisfiable in the theory of arrays since get(set(a 1 , i, e), j) ≈ get(a 1 , j) and get(set(a 2 , i, e), j) ≈ get(a 2 , j). Still W 3 array ∪ L 3 is true in the world L 3 ∪ {get(set(a 1 , i, e), i) ≈ e, get(set(a 2 , i, e), i) ≈ e}.

Termination

Once it has been established that a given set of axioms with triggers is sound and complete for our theory, we must show that the solver equipped with this axiomatization terminates on any ground satisfiability problem. We call such axiomatizations terminating and the rest of this section is dedicated to the definition of this property. There can be no single "true" definition of a terminating axiomatization. Different variations of the solver algorithm may terminate on different classes of problems, which may be more or less difficult to describe and to reason about. We should rather strive for a "good" definition, which, on one hand, leaves room for an efficient implementation, and on the other hand, is simple enough to make it feasible to prove that a given set of axioms is terminating.

Below we present what we consider a reasonably good definition. It serves as the basis for the DPLL-based procedure described in Chapter 4. In Section 2.2, we prove that a non-trivial axiomatization of imperative doubly-linked lists is terminating according to this definition. Finally, in Section 4.2.2, we discuss possible variations of the termination property and their implications for the solver algorithm.

To bring ourselves closer to the implementation, we start by eliminating the existential quantifiers and converting axioms into a clausal form.

Skolemization: The Skolemization transformation, denoted SKO, traverses a formula in topdown order and replaces existential quantifiers with witnesses of Skolem terms as follows:

SKO(∃x.ϕ) � �c(y)� SKO(ϕ[x ← c(y)]),
where y is the set of free variables of ∃x.ϕ and c is a fresh function symbol.

Lemma 2.3. Skolemization preserves feasibility and satisfiability.

Proof. It can be done by induction over ϕ. We construct a world for SKO(ϕ) by giving the Skolem terms the same interpretation as for the corresponding ground terms in the original world for ϕ. In the opposite sense, if SKO(ϕ) is feasible, then ϕ is true in the same world.

The use of the witness is crucial here. Indeed, SKO(∃x.[x]⊥) is �c�[c]⊥ which preserves infeasibility, whereas the formula [c]⊥ is true in any world where c is unknown.

Skolemization may not preserve the soundness and completeness of a set of axioms. For example, if T � is the theory ∃x.P(x), then the Skolemized axiom �c�P(c) is not a sound representation of T � . Indeed, the ground literal ¬P(c) is T � -satisfiable, but the union �c�P(c) ∪ ¬P(c) has no model. This does not present a problem for us: the soundness and completeness theorems in Chapters 3 and 4 do not require Skolemized axiomatizations.

Clausification: Definition 2.8 (Pseudo-clause). We say that a formula is a pseudo-literal if it is a literal l, a trigger [l]C, a witness �l�C, or a universally quantified formula ∀x.C, where C is a disjunction of pseudo-literals, called pseudo-clause.

In what follows, we treat pseudo-clauses (and other kinds of clauses) as disjunctive sets, that is, we ignore the order of their elements and suppose that there are no duplicates. As for traditional logic, any Skolemized formula can be transformed into a clausal form, the case of triggers and witnesses being handled using the equivalences between the formulas [l](ϕ 1 ∧ ϕ 2 ) and [l]ϕ 1 ∧ [l]ϕ 2 , and the formulas �l�(ϕ 1 ∧ ϕ 2 ) and �l�ϕ 1 ∧ �l�ϕ 2 .

Before we proceed to definition of the termination property, let us give some informal explanation of it. To reason about termination, we need an abstract representation of the evolution of the solver's state. It is convenient to see this evolution as a game where we choose universal formulas to instantiate and our adversary decides how to interpret the result of instantiation, that is, what new facts can be assumed. Whenever we arrive at a set of facts that is inconsistent or saturated so that no new instantiations can be made, the game terminates and we win. If, on the other hand, whatever instantiations we do, the adversary can find new universal formulas for us to instantiate, the game continues indefinitely. An axiomatization is terminating if we have a winning strategy for it. In other words, no matter what partial model we explore, there is a sequence of instantiations-which our solver will eventually make due to fairness-leading either to a contradiction or to a saturated partial model. The adversary's moves are represented by so-called truth assignments. Intuitively, given a current set of assumed facts, a truth assignment is any set of further facts that the solver may assume using only propositional reasoning, without instantiation. Once this completion is done, we may choose an assumed universal formula and a known term to perform instantiation, allowing for the next stage of completion and so on. A tree that inspects all possible truth assignments for certain instantiation choices (i.e. all possible adversary's responses to a particular strategy of ours) is called instantiation tree. An axiomatization is terminating if for any ground satisfiability problem we can construct a finite instantiation tree.

To avoid applying substitutions, we use closures. A closure is a pair ϕ • σ made of a pseudoliteral ϕ and a substitution σ mapping every free variable of ϕ to a ground term. We write ϕσ for the application of σ to ϕ, and ∅ for the empty substitution. If two substitutions σ and σ � have the same domain D, we write σ ≈ σ � for the formula

� x∈D xσ ≈ xσ � . If C is a pseudo- clause, we write C • σ for the disjunctive set of closures {ϕ • σ � | ϕ ∈ C
and σ � is σ restricted to the free variables of ϕ}. Disjunctive sets of closures are sometimes called theory clauses, as they come from the axiomatization of our theory of interest.

We define the facts that are readily available from a set of theory clauses V , without the need to eliminate triggers or witnesses, to instantiate a variable, or to decide which part of a disjunction to assume: Definition 2.9. Given a set of theory clauses V, we define the set of literals

�V � � {lσ | l • σ is a unit clause in V }.
Definition 2.10 (Truth assignment modulo T ). A truth assignment of a set of theory clauses V is any set of theory clauses A that can be constructed starting from V by exhaustive application of the following rules:

• if (ϕ 1 ∨. . .∨ϕ n ) • σ ∈ A then add some subset of the closures ϕ 1 • σ , . . . , ϕ n • σ to A, • if [l]C • σ ∈ A and �A� � T lσ then add C • σ to A, • if �l�C • σ ∈ A, then add l • σ and C • σ to A.
We say that a truth assignment A is T -satisfiable if the set of literals �A� is T -satisfiable. A Tsatisfiable truth assignment A is said to be final if every possible instantiation is redundant in A, that is for every closure ∀x.C • σ in A and every term t ∈ T (�A�), there is a ground substitution

σ � such that C • σ � ∈ A and �A� � T (σ ∪ [x � → t]) ≈ σ � .
In what follows, we write T (A) for T (�A�) and A � T l for �A� � T l.

Since truth assignments only decomposes formula and do not introduce new terms, any finite set of theory clauses has a finite number of possible truth assignments.

Remark that, in terms of solver implementation, this definition means that, while we require the solver to eliminate triggers and witnesses eagerly, it is permitted to postpone the decision over disjunctions. Such postponing corresponds to adding no closures at all in the first case of the definition above. In this way, the solver is not urged to make choices which it will have to backtrack later, and can instead wait until subsequent instantiations reduce the choice space. Definition 2.11 (Instantiation tree modulo T ). An instantiation tree of a set of pseudo-clauses W is any tree where the root is labeled by W • ∅, every node is labeled by a set of theory clauses, and every edge is labeled by a non-final truth assignment such that:

• a node labeled by V has leaving edges labeled by all T -satisfiable non-final truth assignments of V,

• an edge labeled by A leads to a node labeled by A ∪C • (σ ∪ [x � → t]), where ∀x.C • σ ∈ A and t ∈ T (A).

We have two notions of termination; the strongest one can be applied to less axiomatizations of theories but leads to easier proof of termination of solvers' implementation: Definition 2.12 (Strong Termination modulo T ). A set of pseudo-clauses W is strongly terminating if, for every finite set of ground literals L, every instantiation tree of W ∪ L is finite. Definition 2.13 (Weak Termination modulo T ). A set of pseudo-clauses W is weakly terminating or simply terminating if, for every finite set of ground literals L, W ∪ L admits at least one finite instantiation tree.

Remark 2.2. In the definition of weak termination, we only require that W has one finite instantiation tree and not that every instantiation tree of W is finite. Indeed, we rely on the solver's implementation to be fair and to do all the instances required by one particular finite instantiation tree. That it may also do other instances is not a problem as, in a finite amount of time, it will either reach an unsatisfiable state or all these unaccounted instances will be redundant.

The process of truth assignment leaves the solver a choice over what parts of a disjunction to assume. It may seem that assuming more formulas will always bring us more known terms and more universal sub-formulas to instantiate, so that it is sufficient to only consider the maximal truth assignments in an instantiation tree. However, this is not true: an assumed formula might be an equality that, instead of expanding the set of known terms, reduces it. Thus it may happen that an infinite branch in an instantiation tree passes through non-maximal truth assignments.

Example 2.2. The proof of termination of the theory of arrays described in Examples 1.1 and 2.1 is straightforward. It suffices to demonstrate that the axioms of W array cannot create new terms. Indeed, let L be a set of ground literals and A a truth assignment of W array ∪ L. Assume that there are three terms a, i, and e in T (�A�) such that set(a, i, e) is known in A, that is �A� ∪ known(T (A)) � T known(set(a, i, e)). Then, for every term t in T (get(set(a, i, e), i) ≈ e), �A� ∪ {get(set(a, i, e), i) ≈ e} ∪ known(T (�A�)) � T known(t). Indeed, since set(a, i, e) is known in A, it must be the case for set(a, i, e) and all it subterms, and, since get(set(a, i, e), i) ≈ e, it is also the case for get(set(a, i, e), i). Thus, no instance of the first axiom can lead to the creation of new known terms. The same reasoning can be done for the second and the third axioms. Therefore, every instantiation tree of W array ∪ L is finite.

Example 2.3. Let us look at a more interesting proof of termination. Consider the following axiomatization. We want to model conversion between two domains E and e such that every element of e can be converted to an element of E but there may be elements of E that cannot be converted to e. For example, this could be used to model conversion between mathematical integers and machine 32 bit integers. The axiomatization contains five function symbols. If valid E (x) (resp. valid e (x)) returns t (for 'true") then x is an element of E (resp. an element of e). The conversion function conv E→e (x) (resp. conv e→E (x)) may return either an element of e (resp. an element of E) or some unspecified "invalid" value, if x is not fit for conversion. If x is an element of E, the function unfit E→e (x) returns t when x cannot be converted to e.

W conv =        ∀x.[valid E (x) ≈ t] valid e (conv E→e (x)) ≈ t ∨ unfit E→e (x) ≈ t ∀x.[valid e (x) ≈ t] valid E (conv e→E (x)) ≈ t ∀x.[valid E (x) ≈ t, valid e (conv E→e (x)) ≈ t] conv e→E (conv E→e (x)) ≈ x ∀x.[valid e (x) ≈ t, conv e→E (x)] conv E→e (conv e→E (x)) ≈ x       
The axiomatization W conv is not strongly terminating. Indeed, if we start from a set of literals L = {valid E (c)}, we can create an infinite instantiation tree by applying in priority the first two axioms. We start by instantiating the first axiom with c. In branches labeled by truth assignments containing valid e (conv E→e (x)) ≈ t • [x � → c], we instantiate the second axiom with conv E→e (c) and so on. As there is an infinite branch in which we can always apply one of the first two axioms, W conv is not strongly terminating.

Still, the axiomatization W conv is weakly terminating. Let L be a finite set of literals. We show how a finite instantiation tree can be constructed for W conv ∪ L. For any truth assignment A, add an instance of one of the two first axioms with a term of L if there is one that is not redundant in A. If there are no more of them, add an instance of one of the two last axioms of W conv if there is one that is not redundant in A. The repeated application of these two steps can only construct finite trees. Indeed, the first one constructs at most two instances per term of L. The second step never adds new terms to A. Indeed, for the last axiom of W conv for example, once the triggers are removed, the only new term is conv E→e (conv e→E (t)) which is equal to t. As a consequence, it constructs at most two instances per term present after the last time the first step was applied.

If neither the first nor the second step can be applied on a satisfiable truth assignment A, every non-redundant instance of the first two axioms in A can only produce new terms of the form unfit E→e (t) with t already in T (A). By contradiction, assume that there is a non-redundant instance of the first axiom with a term t such that A � T valid E (t) ≈ t. By construction of A, t cannot occur in L, otherwise, the instance has been already produced in the first step. As a consequence, valid E (t) ≈ t was deduced using the second axiom and there is t � ∈ T (A) such that A � T valid e (t � ) ≈ t and A � T t ≈ conv e→E (t � ). Therefore, the last axiom of W conv has been instantiated with t � in the second step and A � T conv E→e (conv e→E (t � )) ≈ t � and thus A � T valid e (conv E→e (t)). Consequently, the result of an instance of the first axiom with t can only produce new terms of the form unfit E→e (t) with t already in T (A). Since such terms cannot trigger new instances, we conclude the construction of the instantiation tree by making all non-redundant instances of the four axioms with terms in A, and there is only a finite number of them.

Case Study: Imperative Doubly-Linked Lists

In this section, we give a rather large axiomatization as an example (more than 50 axioms). We assume that the background theory T is the combination of integer linear arithmetic and booleans or any conservative extension of it. The axiomatized theory T � contains a definition of imperative doubly-linked lists with a definition for iterators (named cursors), an equality function, several modification functions and so on. We prove that this axiomatization is sound, complete and strongly terminating. It is inspired by the API of lists in the Ada standard library [START_REF] Dross | Correct code containing containers[END_REF].

Presentation of the Theory

Lists are ordered containers of elements on which an equivalence is defined using the function equal_elements(e 1 : element_type, e 2 : element_type) : bool. We represent imperative lists of elements as pairs of:

• an iterative part: a finite sequence of distinct cursors (used to iterate through the list),

• a content part: a partial mapping from cursors to elements, only defined on cursors that are in the sequence.

� • ← • � • • • • • � • → • � no_element sequence no_element
We define two functions left(co : list, cu : cursor) : list and right(co : list, cu : cursor) : list, that are used to split the list. If cu appears in the iterative part of co or is no_element, left(co, cu) (resp. right(co, cu)) returns the prefix (resp. suffix) of co that stops before (resp. starts at) cu:

� � ↑ . . . ↑ • • � � ↑ . . . ↑ cu • left(co, cu) right(co, cu)
A special empty list empty is returned by left(co, cu) (resp. right(co, cu)) if the cursor cu is f irst(co) (resp. no_element). On no_element, left(co, cu) returns co.

To search the content part of co for the first occurrence of an element e modulo equivalence, we use the function f ind(co : list, e : element_type, cu : cursor) : cursor. If cu appears in the iterative part of co, f ind(co, e, cu) returns the first cursor of co following cu which is mapped to an element equivalent to e. If there is no such element, no_element is returned. To search the whole list co, the cursor no_element can be used instead of f irst(co). contains(co : list, e : element_type) : bool is true if and only if co contains an element equivalent to e.

We add a notion of equality on list: equal_lists(co 1 : list, co 2 : list) is true if and only if both parts of co 1 and co 2 are equal.

The last three functions are designed to describe how a list co is modified when an element is either inserted, deleted or replaced in co. They are represented as predicates relating the value of the list before and after the modification as there may be several possible results for the modification. insert(co : list, cu : cursor, e : element_type, r : list) : bool is true then r can be obtained by inserting a cursor before cu in the list co (or at the end if cu is no_element) and mapping it to e. If delete(co : list, cu : cursor, r : list) : bool is true then r can be obtained by deleting the cursor cu from the list co. If replace_element(co : list, cu : cursor, e : element_type, r : list) : bool is true then r can be obtained by replacing the element associated to cu in co by e.

Description of the Axiomatization

In this section, we formalize the theory of lists by translating it into a first-order axiomatization. We also add triggers and witnesses to make it adequate for integration in our framework. We only give a few axioms. The whole axiomatization is available in Appendix A.1.

The functions length and position are constrained by the axiomatization so that they effectively give a representation of the iterative part of the list. The three following axioms express that a list contains a finite sequence of distinct cursors: Functions on lists such as right, previous, f irst or f ind are only described on their domain of definition. We only present f ind. The function f ind_ f irst(co : list, e : element_type) : cursor returns the result of f ind(co, e, no_element), that is to say the first cursor of co that is mapped to an element equivalent to e. The result f ind(co, e, cu) can then be defined to be the result of f ind_ f irst on the cursors following cu in co that is to say right(co, cu). The predicates describing a modification of the list are only relevant if they are known to be true. Here are axioms describing how the result of a deletion is related to the initial state of the list. They express the links between the two lists using functions length, position and element.

DELETE_RANGE: ∀co 1 , co 2 : list, cu : cursor.[delete(co 1 , cu, co 2 )] delete(co 1 , cu, co 2 ) ≈ t → position(co 1 , cu) > 0 DELETE_LENGTH: ∀co 1 , co 2 : list, cu : cursor.[delete(co 1 , cu, co 2 )] delete(co 1 , cu, co 2 ) ≈ t → length(co 2 ) + 1 ≈ length(co 1 ) DELETE_POSITION_BEFORE: ∀co 1 , co 2 : list, cu 1 cu 2 : cursor.[delete(co 1 , cu 1 , co 2 ), position(co 1 , cu 2 )] (delete(co 1 , cu 1 , co 2 ) ≈ t ∧ position(co 1 , cu 2 ) < position(co 1 , cu 1 )) → position(co 1 , cu 2 ) ≈ position(co 2 , cu 2 ) ∀co 1 , co 2 : list, cu 1 cu 2 : cursor.[delete(co 1 , cu 1 , co 2 ), position(co 2 , cu 2 )] (delete(co 1 , cu 1 , co 2 ) ≈ t ∧ 0 < position(co 2 , cu 2 ) < position(co 1 , cu 1 )) → position(co 1 , cu 2 ) ≈ position(co 2 , cu 2 ) DELETE_POSITION_AFTER: ∀co 1 , co 2 : list, cu 1 cu 2 : cursor.[delete(co 1 , cu 1 , co 2 ), position(co 1 , cu 2 )] (delete(co 1 , cu 1 , co 2 ) ≈ t ∧ position(co 1 , cu 2 ) > position(co 1 , cu 1 )) → position(co 1 , cu 2 ) ≈ position(co 2 , cu 2 ) + 1 ∀co 1 , co 2 : list, cu 1 cu 2 : cursor.[delete(co 1 , cu 1 , co 2 ), position(co 2 , cu 2 )] (delete(co 1 , cu 1 , co 2 ) ≈ t ∧ position(co 2 , cu 2 ) ≥ position(co 1 , cu 1 )) → position(co 1 , cu 2 ) ≈ position(co 2 , cu 2 ) + 1 DELETE_POSITION_NEXT: ∀co 1 , co 2 : list, cu : cursor.[delete(co 1 , cu, co 2 )] delete(co 1 , cu, co 2 ) ≈ t → �next(co 1 , cu)�� DELETE_ELEMENT: ∀co 1 , co 2 : list, cu 1 cu 2 : cursor.[delete(co 1 , cu 1 , co 2 ), element(co 1 , cu 2 )] (delete(co 1 , cu, co 2 ) ≈ t ∧ position(co 2 , cu 2 ) > 0) → element(co 1 , cu 2 ) = element(co 2 , cu 2 )
Note that the axiom DELETE_POSITION_NEXT only introduces a new known term. This term is needed so that the axiomatization is complete. Indeed, consider the following set of ground formulas G:

� length(l 1 ) > 1, insert(l 1 , f irst(l 1 ), e, l 2 ), delete(l 2 , f irst(l 1 ), l 3 ), le f t(l 4 , previous(l 2 , f irst(l 1 ))) ≈ l 1 , right(l 4 , previous(l 2 , f irst(l 1 ))) ≈ l 3 �
It is unsatisfiable in the theory of doubly-linked lists. Indeed, here is the situation it describes:

l 1 : � � � ↑ ↑ . . . ↑ * • • l 2 : e � � � ↑ ↑ ↑ . . . ↑ • * • • l 3 : e � � ↑ ↑ . . . ↑ • • • l 4 : � � � e � � ↑ ↑ . . . ↑ ↑ ↑ . . . ↑ * • • • • •
Since the length of l 1 is strictly greater than 1, there are cursors that appear twice in l 4 (those represented by a •) which is forbidden. Still, without this additional axiom, G ∪W is feasible in our logic. DELETE_POSITION_NEXT introduces a known term for the first cursor of this slice, allowing the axiomatization to deduce that it appears twice in l 4 .

Proofs of Soundness, Completeness, and Termination

In this section, we illustrate how a proof of termination, soundness and completeness can be conducted on the axiomatization of doubly-linked lists.

Theorem 2.2. The axiomatization in Section 2.2.2 is strongly terminating, sound, and complete with respect to thesame axiomatization without the triggers and witnesses.

Proof of Strong Termination

Every universal quantification is done on lists, cursors or elements. As a consequence, if we show that only a finite number of terms of type list, cursor and element_type can be created, we can deduce that the axiomatization is strongly terminating. Let us first look at terms of type list. There is only one formula containing a literal in which there is a sub-term t of type list that does not appear in the trigger, namely FIND_OTHERS. The trigger of this formula is f ind(co, e, cu). Such a term cannot be created by the axiomatization. Since the symbol f ind is not interpreted, known( f ind(co, e, cu)) can only be deduced if we have known( f ind(co � , e � , cu � )) and equalities between all arguments. These equalities are enough to ensure that the new term right(co, cu) is equal to the already known term right(co � , cu � ). As a consequence, there can only be one new term of type list per terms of the form f ind(co, e, cu) in the initial problem.

Then we concentrate on terms of type cursor. The axioms FIND_FIRST, FIND_OTHERS, CONTAINS_DEF, INSERT_NEW, INSERT_NEW_NO_ELEMENT and DELETE_POSITION_NEXT all contain a literal in which there is a sub-term t of type cursor that does not appear in the trigger. Also, there is an existentially quantified cursor variable in EQUAL_LISTS_INV, which amounts to a term of type cursor after Skolemization. All these cases can be solved with the same arguments as for the terms of type list. Indeed, the symbols contains(co, e), f ind(co, e, cu), insert(co 1 , cu, e, co 2 ), delete(co 1 , cu, co 2 ), and equal_lists(co 1 , co 2 ) are all uninterpreted and cannot be created by the axiomatization.

Finally, let us look at terms of type element. There are a great deal of those because the function element is often used. However, most of the time, new terms of type element appear in an equality with an already known term (a sub-term of the trigger). For these terms to be deduced, the equality has to be assumed. Since the equality is with an already known term, the term is not new. The remaining axioms FIND_FIRST_ELEMENT and EQUAL_LISTS_INV can both be solved with the same reasoning we did for terms of type list and cursor. Indeed, f ind_ f irst(co, e) is uninterpreted and can only be created once per contains(co, e) and twice per f ind(co � , e, cu) which themselves cannot be created.

Proof of Soundness

We show that, if a set of literals G has a model in the axiomatization without triggers and witnesses then there is a total model of G and the axiomatization. If I is a model of a set of literals G in the axiomatization without triggers, we define L = {l | I(l) = �}. By construction of L, L is a total model of G. Since L is complete, for every axiom ϕ of the axiomatization, L � T ϕ.

Proof of Completeness

We first need a lemma that states that equalities between integers can safely be added to partial models of the axiomatization: Lemma 2.4. Let L be a world in which the axiomatization is true and t 1 and t 2 ∈ T (L) be two terms of type integer. If L � T t 1 � ≈ t 2 then the axiomatization is also true in L ∪ t 1 ≈ t 2 .

Proof. Triggers of the axiomatization either have no (non-variable) sub-term of type integer or can be written t ≈ t where t is of type integer and has no proper (non-variable) sub-term of this type. In both cases, assuming an equality between two known integer terms cannot make any new sub-term of a trigger become known nor make a trigger itself become true. As a consequence, for every literal l appearing as a trigger in the axiomatization, if

L � � T l, t 1 ,t 2 ∈ T (L) have type integer and L � T t 1 � ≈ t 2 then L ∪ t 1 ≈ t 2 � � T l. This is enough to show that, if the axiomatization is true in L, t 1 ,t 2 ∈ T (L) have type integer and L � T t 1 � ≈ t 2 then the axiomatization is true in L ∪ t 1 ≈ t 2 .
Let G be a set of literals and L a world in which G and the axiomatization are true. We construct a model from Lfor the axiomatization without triggers and witnesses. Since

L � T G, it is also a model of G. Since L is T -satisfiable, let I T be a model of L.
No integer constant appears in a trigger of the axiomatization. As a consequence, the axiomatization is true in L ∪ {i ≈ i | i is an integer constant}. For every term t ∈ T (L) of the form length(co) or position(co, cu), we add t ≈ I T (t) to L. By Lemma 2.4, the axiomatization is still true in L.

For every term co of type list in L, if the term length(co) is not in T (L) modulo T , we add length(co) ≈ 0 to L and, for every term cu of type cursor, if position(co, cu) is not in L, we add position(co, cu) ≈ 0. This amounts to deciding that lists that are not forced to be non-empty are empty and cursors that are not forced to be valid in a list l are not valid in l. The axiomatization is still true in L. Indeed, thanks to POSITION_GTE_ZERO, length(co) is in T (L) whenever there is a cursor cu such that position(co, cu) is in T (L).

We now need to associate a cursor to every position of every non-empty list. For this, we consider zones of lists. We define a zone of a term co of type list in L to be a sublist co[i, j], with 0 ≤ i < j ≤ length(co) such that either i = 0 or there is a term cu of type cursor in T (L) such that L � T position(co, cu) ≈ i and, for all k such that i < k ≤ j, there is no term cu of type cursor in L such that L � T position(co, cu) ≈ k. Remark that elements that are inserted and deleted are, by construction, in a zone of size 1 only containing them (see DELETE_POSITION_NEXT). In the same way, for right and left, cuts are always done at the junction between two different zones.

For every zone z of a list, we define the equivalence class of z, written eq(z), to be the set of the zones that are bound to contain the same cursors as z by literals in L. This computation is straightforward. For example, here are the rules for deletion. For every co[i, j] ∈ eq(z):

L � T delete(co, cu, co � ) ≈ t and L � T j < position(co, cu) → co � [i, j] ∈ eq(z) L � T delete(co, cu, co � ) ≈ t and L � T j > position(co, cu) → co � [i -1, j -1] ∈ eq(z) L � T delete(co � , cu, co) ≈ t and L � T j < position(co � , cu) → co � [i, j] ∈ eq(z) L � T delete(co � , cu, co) ≈ t and L � T j ≥ position(co � , cu) → co � [i + 1, j + 1] ∈ eq(z)
The set eq(z) has some good properties:

1. Every element of eq(z) is a zone.

2. Every zone in eq(z) has the same length.

3. If a zone in eq(z) starts with 0 then they all start with 0.

4. If we have both L � T position(co, cu) > 0 and co[position(co, cu), _] ∈ eq(z) then, for every zone co � [i, _] ∈ eq(z), L � T position(co � , cu) ≈ i.

From the last two properties, we deduce that each list co appears at-most once in eq(z). As a consequence, we can associate a free cursor variable to each position in the equivalent zone of a list without creating lists that may contain the same cursor twice: While there is a zone co[i, j], with co known and i < j:

• We compute the set of zones eq(co[i, j]).

• To each k such that i < k ≤ j, we associate a fresh cursor cu k .

• For each co

� [i � , j � ] ∈ eq(co[i, j]) and each k � such that i � < k � ≤ j � , position(co � , cu k � -i � +i ) ≈ k � is added to L.
Once there is no more zone co[i, j], with co ∈ T (L) and i < j, for every term co of type list and every term of type cursor cu of L for which position(co, cu) is not yet known in L, we can add position(co, cu) ≈ 0 to L. We can check straight-forwardly that the axiomatization is still true in L. We now have a model in the axiomatization without triggers of the iterative part of every list that appears in L.

Let us now consider the content part. Let e be a fresh term of type element. We map element(co, cu) to e for any term co of type list and any term cu of type cursor in L such that element(co, cu) is not in L modulo T . Each axiom with element(co, cu) as a trigger either deduces an equality or an equivalence between new terms, or a non-equivalence between a known term and a new term. As a consequence, the axiomatization is still true in L.

Remark 2.3. Here we are axiomatizing lists of an abstract infinite type. This proof works for any element type with an infinite number of equivalence classes. If the element type has a finite number of equivalence classes, let us call it n, then the axiomatization is not complete anymore. For example, consider the finite set of literals L with n constants of type element e 1 . . . e n containing position(co, f ind_ f irst(co, e i )) > n for every i ∈ 1..n.

We have constructed a model for L the axiomatization described in Section 2.2.2 without the triggers. As a consequence, the axiomatization from Section 2.2.2 is complete for this theory.

Assessment of Adequacy with Respect to Existing Trigger Heuristics

In this section we assess the concordance between our semantics for formulas with triggers and the heuristics used for trigger instantiation in off-the-shelf SMT solvers. We define some program functions for a program API of lists, using contracts. For example, an element can only be accessed on a valid cursor and, after an application of the modification function insert, the new version of the list is related to the old one by the predicate insert. The examples have been written in WhyML so that several provers can be used to discharge the logical formulas arising from their verification using the Why3 tool.

{ has _ element !co cu \/ cu = no _ element } reads { co } writes { co } ensures { insert (old !co) cu e !co }
The tests for using the theory of doubly-linked lists are given in Appendix A.2. Here is only one of them. The function double _ size iterates through the list li, inserting the element e before each existing element of the list. If the list li is not empty at the beginning of the function, then li should be twice as long at the end of the function. Since there is a loop, we need to come up with a loop invariant powerful enough to deduce both that the postcondition is true and that the iteration can be resumed after the insertion. The loop invariant states that:

• the current cursor is valid in li and used to be valid in li at the beginning of the function or no_element was reached,

• the length of the visited part was doubled, and

• the unvisited part of the list li has not been modified yet.

let double _ size (li : ref list) (e : element _ type) = requires { not (is _ empty !li) } ensures { length !li = 2 * (length (old !li)) } let c = ref (first !li) in 'Loop _ Entry: while has _ element !li !c do invariant { (((has _ element (at !li 'Loop _ Entry) !c /\ has _ element !li !c) \/ !c = no _ element) /\ length (left !li !c) = 2 * (length (left (at !li 'Loop _ Entry) !c)) /\ equal _ lists (right !li !c) (right (at !li 'Loop _ Entry) !c)) } insert li !c e; c := next !li !c done
We see on results from Figure 2.1 that, in general, SMT solvers are far less efficient when triggers are removed from the axiomatization. This shows that, even if triggers can be inferred by SMT solvers, efficient handling of first-order formulas usually requires user guidance in this choice. This also validates the fact that our semantics is rather consistent with the way triggers are handled in first-order SMT solvers.

Remark that the test (named test _ false) attempts to prove ⊥. The results on this line are the time needed for each solver to stop returning that it cannot discharge the proof. Out of the three solvers we tried, Alt-Ergo is the only one to terminate on this test in less than 1000s. Indeed, trigger driven instantiation is the only quantifier instantiation heuristics in Alt-Ergo, which is not the case for Z3 and CVC4.

Designing Terminating and Complete Axiomatizations

There is no universal recipe for designing an axiomatization nor for proving its termination or its completeness. Like for designing any decision procedure, the axiomatization and the proofs strongly depend on the theory they decide. In this section, we give good practices, tips, and debugging techniques for designing terminating and complete axiomatizations. We also give several techniques that can be computer assisted using a decision procedure for the background theory T .

Proving Termination of an Axiomatization

Here we list several techniques that can be attempted to do a proof of termination of a given axiomatization W . First, we can define a property stronger than termination which can be checked in an automatable way. The idea is to over-approximate the set of terms that can be created by the axiomatization. For this definition, we need to compute, for any set of ground literals L, the literals that will eventually be entailed in every truth assignment of an instantiation tree of W ∪ L. More precisely, we consider the set of all literals l for which there is an instantiation tree Z of W ∪ L such that l is entailed by every truth assignment at a certain depth in Z. This set is not computable and can be infinite, but we can take some finite under-approximation of it, for example, by fixing the depth, i.e., the number of consecutive instances on a branch. The greater the chosen depth, the more precise our approximation of the set of generated terms and the costlier the computation. In what follows, we assume some fixed depth n: Definition 2.14. Let L be a set of ground literals. Let F 1 , . . . , F m be the sets of theory clauses that can be constructed from (W ∪ L) • ∅ in the following way:

• if [l]C • σ ∈ F and �F� � T l then add C • σ to F, • if �l�C • σ ∈ F then add l • σ and C • σ to F, and • if ∀x.C • σ ∈ F, t ∈ T (�F�)
, and there are less than n instances that have already been added to

F then add C • (σ ∪ [x � → t]) to F.
We define the set of literals reachable with n instances from L written RCH n W (L) to be

�F 1 ∪ • • • ∪ F m �.
Now, we can construct a single partial instantiation tree where all the literals from RCH n W (L)� are entailed in every branch after a certain number of instances. Here and below, a partial instantiation tree is an instantiation tree in which no instance has been chosen yet for some truth assignments.: Lemma 2.5. For every set V of theory clauses such that W • ∅ ⊆ V and �V � � T L, there is a finite partial instantiation tree Z of V such that, for every truth assignment A that labels a orphan edge of Z, �A� � T RCH n W (L).

Proof. Let F 1 , . . . , F m be the sets of theory clauses that were used for the construction of RCH n W (L). We show by induction over the construction of a set F i that, for every set V � of theory clauses such thatV ⊆ V � , there is a finite partial instantiation tree Z i (V � ) of V � such that, for every truth assignment A that labels a orphan edge of Z i (V � ) and every theory clause

C • σ in F i \ (L • ∅), we have �A� ∪ known(T (�A�)) � T known(T (σ )) and there is C • σ � ∈ A such that �A� � T σ ≈ σ � .
This is enough to conclude as we can construct Z iteratively starting from Z 0 = V and, for i < m, extending every orphan edge of Z i labeled by a truth assignment A with Z i+1 (A).

Let us now do the proof. By hypothesis, W • ∅ ⊆ A.

• If C • σ or l • σ was added to F using �l�C • σ ∈ F then, by induction hypothesis, for every satisfiable truth assignment A at a leaf in Z, �A� ∪ known(T (�A�)) � T known(T (σ )) and there is σ � such that �l�C • σ � ∈ A and �A� � T σ ≈ σ � . As a consequence, by definition of a truth assignment, l • σ � and C • σ � are in A. Thus, the property holds for the tree Z.

• If C •σ was added to F using [l]C •σ ∈ F then �F� � T l. By induction hypothesis, for every satisfiable truth assignment A at a leaf in Z, �A� ∪ known(T (�A�)) � T known(T (σ )) and there is σ � such that [l]C • σ � ∈ A and �A� � T σ ≈ σ � . What is more, for every l � • σ � ∈ F, �A� � T l � σ � by induction hypothesis.
As a consequence, �A� � T l and, by definition of a truth assignment, C • σ � is in A. Thus, the property holds for the tree Z.

• If C • (σ ∪ [x � → t]
) was added to F using ∀x.C • σ ∈ F and t ∈ T (�F�) then, by induction hypothesis, for every satisfiable truth assignment A at a leaf in Z, we have �A� ∪ known(T (�A�)) � T known(T (σ )) and there is

σ � such that ∀x.C • σ � ∈ A and �A� � T σ ≈ σ � . What is more, by induction hypothesis, for every l � • σ � ∈ F, �A� � T l � σ � . Thus, �A� ∪ known(T (�A�)) � T known(T (t)) and there is t � in T (�A�) such that �A� � T t ≈ t � .
We construct a new tree Z � starting from Z by adding a new instance ϕ

• (σ � ∪ [x � → t � ]) for every satisfiable truth assignment A at a leaf in Z such that ϕ • (σ � ∪ [x � → t � ]) is not redundant in A.
By construction, the property holds for the tree Z � .

We can then define an over-approximation of all the terms that can be learned by instantiating axioms and removing triggers from W . The idea is to use fresh constant symbols to model terms that have been used to instantiate a universal quantifier: Definition 2.15. For every pseudo-clause C and every set of literals G, we define an overapproximation N W (C, G) of the set of new terms that can be deduced from C assuming that the elements of G are true and all their subterms are known:

N W (l, G) = {t ∈ T (l) | RCH n W (G ∪ l) ∪ known(T (RCH 0 W (G))) � T known(t)} N W (�l�C, G) = N W (l, G) ∪ N W (C, G ∪ l) N W ([l]C, G) = N W (C, G ∪ l) N W (∀x.C, G) = N W (C[x � → a], G ∪ a ≈ a)
a is a fresh constant symbol

N W (C, G) = � ϕ∈C N W (ϕ, G)
We then define the set of new terms of W to be

� C∈W N W (C, ∅). Remark 2.4.
We do not consider terms that have allowed us to instantiate universal quantifiers and to remove triggers as they must already be known to reach the current sub-formula nor for terms that can be equated to one of those in every truth assignment in less than n instances. Proof. Let L be a set of literals. We show that (W ∪ L) • ∅ has a finite instantiation tree. Let S be T (L) ∪ {t | t is a ground term of W }. We define the set S of the ground terms that appear in an instance of a sub-formula of W with terms of S. More precisely, S is the smallest set of ground terms such that, for every literal l or every witness �l�_ that appears as a sub-formula of W and every substitution σ from free variables of l to terms of S, T (lσ ) ⊆ S. Let N be the set of pairs (C, G) of a pseudo-clause and a set of literals on which N W is called during the computation of

� ϕ∈W N W (ϕ, ∅).
We construct a finite instantiation tree of (W ∪ L) • ∅ in the following way. For a satisfiable, non-final truth assignment A, if we have �A� ∪ known(S) � T known(T (�A�)), then we add any non-redundant instance to A. Otherwise, we show that there is a term t ∈ S such that �A� ∪ known(S) � T known(t) and there is a finite partial instantiation tree Z starting from A such that, for every truth assignment A � at a orphan edge of Z, �A � � ∪ known(S) � T known(t). We attach Z to A and continue.

This method terminates and gives a finite instantiation tree. Indeed, at each step, one of the following conditions holds:

• There is one new non-redundant instance of a universally quantified sub-formula of W with a term from S. There is only a finite number of such instances.

• There are more terms of S that are equal to a term of S modulo A � than modulo A. Of course the number of terms in S is also finite.

In the rest of the proof, we show that, if �A� ∪ known(S) � T known(T (�A�)), there is a term t ∈ S such that �A� ∪ known(S) � T known(t) and there is a finite partial instantiation tree Z starting from A such that, for every truth assignment A � at a orphan edge of Z, �A � �∪known(S) � T known(t). We need an intermediate lemma:

Lemma 2.6. Let A be a truth assignment in an instantiation tree Z of (W ∪ L) • ∅. If �A� ∪ known(S) � T known(T (�A�)), then there is a pair (l, G) ∈ N and a substitution µ from constant symbols introduced during the computation of N W to terms of S such that �A� ∪ known(S) � T known(T (lµ)), �A� ∪ known(S) � T known(T (Gµ)), and �A� � T Gµ ∪ lµ.

Proof. We show by induction over the construction of A that, for every element C • σ / ∈ L • ∅ that is added to a truth assignment A � above A in Z, there is (Cτ, G) ∈ N where τ maps free variables to constant symbols and a substitution µ from constant symbols to terms such that τ µ| vars(C) is σ and �A � � � T Gµ. For simplicity, we divide handling of a witness �l�C • σ in the construction of a truth assignment into two parts: we first add l • σ and then

C • σ . If C ∈ W then (C, ∅) ∈ N.
Otherwise, one of the following holds:

• ϕ • σ was added to a truth assignment A � in Z such that ϕ • σ ∨ C � • σ � ∈ A � . By induction hypothesis, there is ((ϕ ∨ C � )τ, G) ∈ N
where τ maps free variables to constant symbols and a substitution µ from constant symbols to terms such that τ µ| vars(ϕ∨C � ) is σ ∪ σ � and �A � � � T Gµ. By definition of N W , we have (ϕτ, G) ∈ N.

• l • σ was added to a truth assignment A � in Z such that �l�C • σ ∈ A � . By induction hypothesis, there is (�l�Cτ, G) ∈ N where τ maps free variables to constant symbols and a substitution µ from constant symbols to terms such that τ µ| vars(�l�C) is σ and �A � � � T Gµ.

By definition of N W , (lτ, G) ∈ N.

• C • σ was added to a truth assignment A � in Z such that �l�C • σ ∈ A � . By induction hypothesis, there is (�l�Cτ, G) ∈ N where τ maps free variables to constant symbols and a substitution µ from constant symbols to terms such that τ µ| vars(�l�C) is σ and

�A � � � T Gµ. By definition of N W , (Cτ, G∪lτ) ∈ N. What is more, since �l�C •σ ∈ A � , we have l •σ ∈ A � and, thus, �A � � � T (G ∪ lτ)µ. • C • σ was added to a truth assignment A � in Z such that [l]C • σ ∈ A � and �A � � � T lσ .
By induction hypothesis, there is ([l]Cτ, G) ∈ N where τ maps free variables to constant symbols and a substitution µ from constant symbols to terms such that τ µ| vars(

[l]C) is σ and �A � � � T Gµ. By definition of N W , (Cτ, G ∪ lτ) ∈ N. Since �A � � � T lσ , we have �A � � � T (G ∪ lτ)µ. • the new instance C • (σ ∪ [x � → t]) was added to a truth assignment A � in Z such that ∀x.C • σ ∈ A � and t ∈ T (�A � �)
. By induction hypothesis, there is (∀x.Cτ, G) ∈ N where τ maps free variables to constant symbols and a substitution µ from constant symbols to terms such that τ µ| vars(∀x.C) is σ and

�A � � � T Gµ. By definition of N, there is a new constant symbol a such that (C(τ ∪ [x � → a]), G ∪ a ≈ a) ∈ N. Since a is fresh in Cτ and G, we extend µ with [a � → t]. Since t ∈ T (�A � �), we have �A � � � T (G ∪ a ≈ a)(µ ∪ [a � → t]).
Now, we only need to find a closure (l • σ ) ∈ A with an associated pair (lτ, G) ∈ N and a substitution µ from constant symbols to terms of S such that �A� ∪ known(S) � T known(T (lσ )) and �A� ∪ known(S) � T known(T (Gµ)). Let us consider the first time we add a closure l • σ such that �A� ∪ known(S) � T known(T (lσ )) to a truth assignment

A � above A in Z. Since T (L) ⊆ S, l • σ / ∈ L • ∅
and, as we have just shown, there is (Cτ, G) ∈ N where τ maps free variables to constant symbols and a substitution µ � from constant symbols to terms such that

τ µ � | vars(C) is σ and �A � � � T Gµ � . What is more, by definition of l •σ , we have �A�∪known(S) � T known(T (�A � �)) and, since �A � � � T Gµ � , we have �A� ∪ known(S) � T known(T (Gµ � )).
Finally, we take a substitution µ from constant symbols to terms of S that is equal to µ � modulo �A�. Since �A� ∪ known(S) � T known(T (µ � )), there is one. By definition of µ, �A� ∪ known(S) � T known(T (Gµ)) and �A� � T Gµ ∪ lµ.

As �A� ∪ known(S) � T known(T (�A�)), there are a pair (l, G) ∈ N and a substitution σ such that �A� ∪ known(S) � T known(T (lσ )), �A� ∪ known(S) � T known(T (Gσ )), and �A� � T Gσ ∪ lσ by Lemma 2.6. Let t be a term of l such that �A� ∪ known(S) � T known(tσ ), since every element of σ appears in T (Gσ ), there must be such a term. Notice that, by definition of N W , every variable of I freely occurs in G. Also notice that, by definition of S and σ , tσ ∈ S. It now remains to show that there is a finite partial instantiation tree Z starting from A such that, for every truth assignment A � at a orphan edge of Z, �A � � ∪ known(S) � T known(tσ ).

Since W has a finite set of new terms � ϕ∈W N W (ϕ, ∅), no term of T (l) that contains a constant introduced by N W can appear in the set of new terms of W . Since �A� ∪ known(S) � T known(tσ ), the term t contains at least a constant introduced by N W . As a consequence, we have that RCH n W (G∪ l) ∪known(T (RCH 0 W (G))) � T known(t). Replacing constant symbols with terms using σ , we get

RCH n W (G ∪ l)σ ∪ known(T (RCH 0 W (G)))σ � T known(t)σ . By immediate induction on the construction of RCH n W (G ∪ l), we have both RCH n W (G ∪ l)σ ⊆ RCH n W (Gσ ∪ lσ ) and T (RCH 0 W (G))σ ⊆ T (RCH 0 W (G)σ ) ⊆ T (Gσ )∪S. Since �A�∪known(S) � T known(T (Gσ )), we have RCH n W (Gσ ∪ lσ ) ∪ �A� ∪ known(S) � T known(tσ )
. By Lemma 2.5, since �A� � T Gσ ∪ lσ , we can continue the instantiation tree Z after A so that, for every satisfiable truth assignment A � labeling a orphan edge after A, we have For any natural n in the definition of RCH, we have that � ϕ∈W array N W array (ϕ, ∅). For example, if n = 0 then, for the first axiom, we have:

�A � � � T RCH n W (Gσ ∪ lσ ) and, thus, �A � � ∪ known(S) � T known(tσ ).
N W array (∀a, i, e.[set(a, i, e)] (get(set(a, i, e), i) ≈ e), ∅) = N W array ([set(a, i, e)] (get(set(a, i, e), i) ≈ e), {a ≈ a, i ≈ i, e ≈ e})
= N W array ((get(set(a, i, e), i) ≈ e), {set(a, i, e) ≈ set(a, i, e), a ≈ a, i ≈ i, e ≈ e})

= T �� t ⊆ T (get(set(a, i, e), i) ≈ e)) | get(set(a, i, e), i) ≈ e ∪ T ({set(a, i, e), a, i, e}) � T known(t) �� = ∅
If we remove the trigger from this axiom, then it can produce a new term set(a, i, e) as we have:

N W array (∀a, i, e. (get(set(a, i, e), i) ≈ e), ∅) = T �� t ⊆ T (get(set(a, i, e), i) ≈ e)) | get(set(a, i, e), i) ≈ e ∪ T ({ a, i, e}) � T known(t) �� = {set(a, i, e)}
Example 2.5. The axiomatization W conv presented in Example 2.3 does not have finitely many new terms. Indeed, there are terms that can be created by W conv and that will not be equated to already existing terms.

Even if an axiomatization W does not have the above stated property, it is sometimes enough to go through the set of new terms from W that contain free variables to justify that only a finite number of them can be created. This is what we do for the proof of termination of the axiomatization for doubly-linked lists: We go through the new terms of the axiomatization that can be used to instantiate universally quantified formulas of W and we justify that there can only be a finite number of those. Such a proof can be complex, in particular if quantification is done on interpreted sortswhich is not the case for doubly-linked lists. It can be simplified further if the triggers of the axiomatization only contain uninterpreted function symbols. Indeed, in this case, it is enough to show that there can only be a finite number of new terms starting with function symbols that appear in a trigger. Such a proof can sometimes be done modularly. The idea is to find a partition S 0 . . . S n of the set of function symbols that appear in triggers in an axiomatization W such that, if W i is the subset of formulas of W using symbols of S i in triggers, W i can only create new terms starting with symbols from S 0 ∪ • • • ∪ S i . We can then show separately that, for each set S i , formulas of W i cannot create an infinite number of new terms starting with function symbols in S i .

Note that this reasoning can be strengthened to only check for new terms that actually match triggers in the axiomatization. Still, in this case, reasoning must be done modulo equality. This is even more difficult if triggers contain terms of an interpreted sort, since equality between such terms can be deduced by theory reasoning.

Designing a Complete Axiomatization

The natural way to prove the completeness of an axiomatization W is to give a general method for completing a world L in which W is true into a model of the theory. This implies that terms of L that are interpreted must be given a value in this world which may create new equalities between them. The reasoning is much easier if these equalities cannot unlock new triggers in W . In multi-sorted logic, it is enough to restrict triggers so that, for every trigger l in the axiomatization and every subterm t of l of an interpreted sort, either t is a variable or t is toplevel in l. Indeed, the only triggers that can be unlocked are then those where l becomes true because of the new equalities, which are generally much easier to reason about. For example, we use this technique in the proof of completeness of the theory of doubly-linked lists to show that adding an equality between known integer terms cannot unlock new deductions. Constants of interpreted types can be allowed in triggers without losing this property if they are known in every world in which the axiomatization is true.

Another important point is that, when a trigger is guarding a disjunction, triggers should generally not prevent us from deducing an element of the disjunction when the others are false. In the same way, if an element of the disjunction is an equality, then the rewriting should be possible both ways. For example, in the theory of non-extensional arrays, we duplicated the second axiom so that there is a trigger coming from each side of the equality: ∀a, i, j, e.[get(set(a, i, e), j)] (i � ≈ j → get(set(a, i, e), j) ≈ get(a, j)) ∀a, i, j, e.[set(a, i, e), get(a, j)] (i � ≈ j → get(set(a, i, e), j) ≈ get(a, j))

In some cases, there are good reasons not to apply this rule. It allows to orient deduction and rewriting. Still, if such a choice is made, then there can be predicates that cannot be deduced to be true and terms that cannot be deduced to be known even if they are entailed by the theory. Literals that are entailed by the axiomatized theory, yet cannot be deduced in the axiomatization, should not appear in triggers.

Example 2.6. Consider triggers containing the function symbol equal_lists in the theory of doubly-linked lists. We cannot deduce that equal_lists(co 1 , co 2 ) ≈ t is true for two lists co 1 and co 2 if the term equal_lists(co 1 , co 2 ) does not appear in the context, since every axiom involving a term starting with equal_lists has this term as a trigger.

In general, equal_lists should not be used as a trigger if we want a complete axiomatization. Adding the following axiom for prefix of a list would be at the cost of the completeness of our theory:

IS_PREFIX_INV: ∀co 1 , co 2 : list.[is_pre f ix(co 1 , co 2 )] is_pre f ix(co 1 , co 2 ) � ≈ t → (∀cu : cursor.[equal_lists(co 1 , le f t(co 2 , cu))] (has_element(co 2 , cu) ≈ t ∨ cu ≈ no_element) → equal_lists(co 1 , le f t(co 2 , cu)) � ≈ t)
For example, consider the unsatisfiable set of literals L = {is_pre f ix(empty, co) � ≈ t}. We cannot deduce that L is unsatisfiable in our theory using our axiom as we do not generate the term equal_lists(empty, le f t(co, f irst(co))).

A notable exception to this principle are defining axioms. Axioms used to provide an uninterpreted symbol f with its meaning can generally use f in their triggers even though we cannot deduce the value of a term starting with f in the axiomatization every time we can deduce it in the theory. For example, the axiom EQUAL_LISTS_LENGTH has equal_lists(co 1 , co 2 ) as a trigger and yet the axiomatization is complete. Indeed, in the theory of doubly-linked lists, we cannot deduce that two lists are equal if we do not know a priori that they have the same length. Thus, every world in which the axiomatization is true can be completed by assuming equal_lists(co 1 , co 2 ) � ≈ t whenever we do not have length(co 1 ) ≈ length(co 2 ).

EQUAL_LISTS_LENGTH:

∀co 1 , co 2 : list.[equal_lists(co 1 , co 2 )] equal_lists(co 1 , co 2 ) ≈ t → length(co 1 ) ≈ length(co 2 )

An Automatable Debugger for Completeness

When designing an axiomatization W , it may be useful to have an automatable way to search for counter-examples to the completeness of W with respect to the first-order axiomatization W � which is W where triggers are translated into implications. More precisely, we look for sets of literals L such that L ∪W � is unsatisfiable in first-order logic but there is a world in which L ∪W is true. The algorithm below searches systematically for sets of literals L such that L ∪ W � is unsatisfiable in first-order logic. An implementation of the solver can then be used to decide if there is a world in which L ∪W is true (see Definition 2.7).

The idea is to paramodulate between axioms of W , seen as clauses with free variables, to deduce new clauses. Every new clause can then be negated to produce a potential counterexample. Let G be a set of clauses with free variables. We use lazy paramodulation: for

C 1 ∨ f (t 1 , . . . ,t n ) ≈ s and C 2 ∨ A[ f (t � 1 , . . . ,t � n )] ∈ G, we produce C 1 ∨C 2 ∨ t 1 � ≈ t � 1 ∨ • • • ∨ t n � ≈ t � n ∨ A[s]. Example 2.7.
The two first-order axioms of the theory of arrays are converted into the two clauses get(set(a, i, e), i) ≈ e and i ≈ j ∨ get(a, j) ≈ get(set(a, i, e), j) where a, i, j, and e are free variables. We can then use the above algorithm to infer the counter-examples that we produced in Example 2.1. The first axiom coupled with itself gives:

set(a 1 , i 1 , e 1 ) � ≈ set(a 2 , i 2 , e 2 ) ∨ i 1 � ≈ i 2 ∨ e 1 ≈ e 2
So we deduce a potential counter-example {set(a 1 , i, e 1 ) ≈ set(a 2 , i, e 2 ), e 1 � ≈ e 2 } which is indeed a counter-example for the axiom with a bad trigger in Example 2.1: ∀a, i, e.[get(set(a, i, e), i)] get(set(a, i, e), i) ≈ e

The second axiom contains two occurrences of get. Rewriting the equality both ways in each occurrence of get gives the four following clauses. The first and the fourth give the counterexamples for the other two cases in Example 2.1:

i 1 ≈ j ∨ i 2 ≈ j ∨ set(a 1 , i 1 , e 1 ) � ≈ set(a 2 , i 2 , e 2 ) ∨ get(a 1 , j) ≈ get(a 2 , j) i 1 ≈ j ∨ i 2 ≈ j ∨ get(a, j) ≈ get(set(set(a, i 1 , e 1 ), i 2 , e 2 ), j) i 1 ≈ j ∨ i 2 ≈ j ∨ get(set(set(a, i 2 , e 2 ), i 1 , e 1 ), j) ≈ get(a, j) i 1 ≈ j ∨ i 2 ≈ j ∨ get(set(a 1 , i 1 , e 1 ), j) ≈ get(set(a 2 , i 2 , e 2 ), j)
We see that this automatable approach manages to find all the counter-examples we used in Example 2.1.

Conclusion

We have given a framework for reasoning about soundness, completeness, and termination of a first-order axiomatization with triggers. We have seen in Section 2.2 that our semantics is close, but not identical, to the behavior of existing solvers. We should now implement an SMT solver that supports first-order logic with triggers and transforms a sound, complete, and terminating axiomatization of a theory T � into a decision procedure for satisfiability modulo T � . In Chapter 2 we introduced a semantics for first-order logic with triggers as well as notions of soundness, completeness, and termination of axiomatizations in this logic.

In this chapter, we present a theoretical way of extending a generic ground SMT solver so that it can turn an axiomatization with triggers into a decision procedure. It requires that the axiomatization is sound and complete in the framework defined in Chapter 2, and that it is strongly terminating as defined in 2.12.

Description

In this section, we define a wrapper over a generic SMT solver for ground formulas that accepts a theory written as a set of formulas with triggers. This solver is a theoretical model and it is not meant to be efficient. We prove it sound with respect to our framework.

Preliminaries

We extend an existing solver S that decides satisfiability of ground formulas modulo T . To be able to reason about it, we make a few assumptions about the interface of the ground solver S:

• S returns Unsat(U) when called on an unsatisfiable set of ground formulas G where U is an unsatisfiable core of G, that is, a subset of G such that U � T ⊥.

• S returns Sat(L) when called on a satisfiable set of ground formulas G. We assume that there is a model I of G in T such that L is exactly the set of literals of G that are true in I.

We describe a solver L ift(S) that takes a set of first-order axioms with triggers and witnesses, denoted Ax, and a set of ground clauses, denoted G. Before starting the procedure, we Skolemize and clausify the axioms in Ax, producing a set of pseudo-clauses W .

To use S to reason about formulas with triggers and witnesses, we need to encode theory clauses coming from the axiomatization into regular clauses. Definition 3.1. To each pseudo-literal ϕ in W ∪ G, we associate an atomic formula B ϕ = P ϕ (x), where P ϕ is a fresh predicate symbol and x are the free variables of ϕ. We define an encoding of closures into literals in the following way:

�l • σ � � lσ �ϕ • σ � � B ϕ σ if ϕ is not a literal A literal B ϕ σ is called an opaque literal.
To model the trigger mechanism, we need a way to protect a theory clause so that its elements are not available until a certain condition is fulfilled. We define a guarded clause as a pair H → C, where the guard H is a conjunctive set of closures and C is a theory clause. We extend our encoding to guarded clauses:

�H → C� � � ϕ•σ ∈H ¬ �ϕ • σ � ∨ � ϕ•σ ∈C �ϕ • σ �.
The solver L ift(S) for the theory T � maintains a set of guarded clauses R. It starts with R = (G ∪ W ) • ∅. It repeatedly launches the solver S on �R� and then, if S returns Sat(L), it uses the set of literals L to augment R with new guarded clauses by reasoning on formulas with triggers and witnesses. If S returns Unsat(U), L ift(S) returns Unsat.

To deduce new guarded clauses that should be added to R, L ift(S) interprets ground models of �R� returned by S as sets of closures. L ift(S) must prune the set of ground literals L returned by S in Sat(L) to only consider relevant ones: Definition 3.2. Let L be a world and R be a set of guarded clauses. We define the set of closures that are relevant in L, written L| R , to be the limit of a sequence of sets of closures {M i } such that M 0 = ∅ and M n+1 is the biggest superset of M n such that, for every closure ϕ • σ ∈ M n+1 :

• �ϕ • σ � ∈ L, and • there is a guarded clause H → C ∈ R such that ϕ • σ ∈ C and H ⊆ M n .
We say that a guarded clause

H → C is relevant in L if H ⊆ L| R .
To comply with the semantics of triggers, when the solver S returns Sat(L) on a set of ground clauses �R�, we need to be able to decide whether L| R should allow to deduce

C • σ from a trigger [l]C • σ . We use S to compute a subset M � of L| R such that �M � � � l if any. First, we launch S on known(T (�L| R �)) ∪ �L| R � ∪ {¬l ∨ ¬known(T (l))}. Since �L| R � is a subset of L and therefore is satisfiable, S can either return Sat(L � ) or Unsat(U ∪ {¬l ∨ ¬known(T (l))}). If it returns Sat(L � ), then �L| R � � � l.
Otherwise, we compute a subset M � of L| R such that, for every literal known(t) in U, there is a closure l • σ ∈ M � such that t ∈ T (lσ ), and, for every other literal l in U, there is

a closure l � • σ ∈ M � such that l = l � σ . POS UNFOLD �l�C • σ ∈ L| R �l�C • σ → C • σ �l�C • σ → l • σ NEG UNFOLD [l]C • σ ∈ L| R M � ⊆ L| R �M � � � lσ [l]C • σ ∧ � M � → C • σ INST ∀x.C • σ ∈ L| R l • σ � ∈ L| R t ∈ T (lσ � ) �L| R � ∪ {B ϕ σ | ϕ • σ ∈ L| R } ∪ {¬B ϕ (σ ∪ [x � → t]) | ϕ ∈ C} � T ⊥ ∀x.C • σ ∧ l • σ � → C • (σ ∪ [x � → t])

Deduction Rules for First-Order Logic with Triggers

The algorithm for L ift(S) is as follows:

1. Initialize R to (W ∪ G) • ∅.
2. Call S on �R�.

3. If S returns Unsat(U) then return Unsat.

If S returns Sat(L):

• If no new guarded clause can be inferred from L using deduction rules from Figure 3.1 then return Sat.

• If at least one new clause can be inferred from L using POS UNFOLD or NEG UN-FOLD, let R � be a non-empty set of new clause inferred from L using these rules.

• Otherwise, let R � be a non-empty set of new clauses inferred from L using INST.

• Go to step 2 with R := R ∪ R � .
Deduction rules in Figure 3.1 are used to add to the set of closures L| R elements that can only be deduced from it using first-order logic with triggers. Still, since elements of L| R are not implied by R, every deduced formula needs to be guarded so that it is a tautology in first-order logic with triggers, and the satisfiability of R does not change.

The rule POS UNFOLD states that a witness �l�C implies both the literal l and the pseudoclause C.

In the rule NEG UNFOLD, we remove the trigger blocking a pseudo-clause. For this rule to produce a tautology, we need to guard it with a set of closures M � that imply the validity of the trigger in first-order logic with triggers, namely �M � � � l. The set M � is computed using S as explained in the previous section.

The rule INST instantiates a universally quantified formula with a term of �L| R �. To ensure termination, we need to be careful not to produce redundant instances, following the Definition 2.10 of the previous chapter. For this check we also use S. We only produce an instance of

∀x.C • σ with a term t if �L| R � ∪ {B ϕ σ | ϕ • σ ∈ L| R } is not a model of � ϕ∈C B ϕ (σ ∪ [x � → t]). In particular, if R contains a guarded clause H → C • µ relevant in L such that �L| R � � T σ ∪ [x � → t] ≈ µ then L| R contains ϕ • µ for some ϕ ∈ C and �L| R � ∪ {B ϕ σ | ϕ • σ ∈ L| R } is a model of � ϕ∈C B ϕ (σ ∪ [x � → t]
). Notice that we convert every closure in L| R and C • (σ ∪ [x � → t]), even closures of literals, into an opaque literal. Indeed, to ensure completeness, our redundancy check must not block instantiations that may bring in new terms even if they are redundant from the logical point of view.

Example 3.1. Here is a possible execution of the solver L ift(S) on the set of ground formulas G and an axiomatization W . We assume that S is a decision procedure for the background theory T combining linear integer arithmetic and Equality with Uninterpreted Functions:

G = { f (0) ≈ 0, f (1) � ≈ 1} W = {∀x.[ f (x + 1)] f (x + 1) ≈ f (x) + 1}
Let us show how the solver L ift(S) can deduce that

R 0 = � f (0) ≈ 0 • ∅, f (1) � ≈ 1 • ∅, ∀x.[ f (x + 1)] f (x + 1) ≈ f (x) + 1 • ∅ � is unsatisfiable.
1. The ground solver returns Sat(L 0 ) on �R 0 �, where L 0 is �R 0 � itself. Thus, we compute

L 0 | R 0 to be (G ∪ W ) • ∅. We have f (0) ≈ 0 • ∅ ∈ L 0 | R 0 and 0 ∈ T ( f (0) ≈ 0).
As a consequence, the rule INST can instantiate x with 0 in the universal formula:

R 1 = R 0 ∪    ∀x. [ f (x + 1)] f (x + 1) ≈ f (x) + 1 • ∅ ∧ f (0) ≈ 0 • ∅ → [ f (x + 1)] f (x + 1) ≈ f (x) + 1 • [x � → 0]    2. S returns Sat(L 1 ) on �R 1 � with L 1 = L 0 ∪ {�[ f (x + 1)] f (x + 1) ≈ f (x) + 1 • [x � → 0]�}. We compute L 1 | R 1 to be L 0 | R 0 ∪ {[ f (x + 1)] f (x + 1) ≈ f (x) + 1 • [x � → 0]}.
Based on results from the theory of arithmetics, the ground solver can deduce that we have

{ f (0) ≈ 0, f (1) � ≈ 1} � f (0+1) ≈ f (0+1).
Thus, the rule NEG UNFOLD can add another formula to R 1 :

R 2 = R 1 ∪    [ f (x + 1)] f (x + 1) ≈ f (x) + 1 • [x � → 0] ∧ f (0) ≈ 0 • ∅ ∧ f (1) � ≈ 1 • ∅ → f (x + 1) ≈ f (x) + 1 • [x � → 0]    3.
The ground solver returns Unsat on �R 2 �. Indeed, any model of �R 2 � would satisfy f (0 + 1) ≈ f (0) + 1, f (0) ≈ 0 and f (1) � ≈ 1. Thus, the initial set G is unsatisfiable modulo W .

Soundness, Completeness, and Termination

In this section, we prove that our solver can turn a sound, complete, and strongly terminating axiomatization Ax of a theory T � into a decision procedure for T � .

Soundness and Completeness

Lemma 3.1. Let H → C be a new guarded clause that can appear as the conclusion of a deduction rule in Figure 3

.1. For world L, if L � � ϕ•σ ∈H ϕσ then L � � ϕ•σ ∈C ϕσ . Proof. The guarded clauses �l�C • σ → C • σ and �l�C • σ → l • σ can be deduced by POS UN- FOLD. If L � �l�Cσ then L � Cσ and L � lσ . The ground clause [l]C • σ ∧ � M � → C • σ can be deduced by NEG UNFOLD if �M � � � lσ . If L � {ϕσ � | ϕ • σ � ∈ M � } then L � lσ . Thus, if L � [l]Cσ and L � {ϕσ � | ϕ • σ � ∈ M � } then L � Cσ . The clause ∀x.C • σ ∧ l • σ � → C • (σ ∪ [x � → t]) can be deduced by INST if t ∈ T (lσ � ). If L � lσ � then L ∪ known(T (L)) � T known(T (t)). Thus, if L � ∀x.Cσ and L � lσ � then there is t � ∈ known(T (L)) such that L � C(σ ∪ [x � → t � ]) and L � T t ≈ t � . By immediate induction on the structure of elements of C, L � C(σ ∪ [x � → t]).
Lemma 3.2. Let W ∪ G a set of pseudo-clauses. Let R be the set of theory clauses that have been computed by our algorithm after some steps starting from (W ∪ G) • ∅ and let L be a set of ground literals returned by S on �R� in Sat(L). For every closure ϕ

•σ ∈ L| R , T (σ ) ⊆ T (�L| R �).
Proof. We do the proof by induction over the construction of the set of relevant literals. For every ϕ The only interesting rule is INST, since it introduces a new term t in the substitution σ of its positive literals. Since t has to be in T (�L| R �) for INST to be applied, we have Proof. We define W to be the result of the Skolemization and the clausification of Ax. Every model of Ax can be extended to a model of W by adding the interpretations of the Skolem functions. As a consequence, since Ax is sound, for every T � -satisfiable set of literals G � that only contains literals of G, there is a model of W ∪ G � .

• σ ∈ L| R there is a guarded clause H → C ∈ R such that ϕ • σ ∈ C and, for every closure ϕ � • σ � ∈ H, ϕ � • σ � was added to L| R before ϕ • σ . As a consequence, it is enough to show that, for every clause H → C ∈ R, if, for every closure ϕ � • σ � ∈ H, T (σ � ) ⊆ T (�L| R �) then, for every positive closure ϕ • σ ∈ C, T (σ ) ⊆ T (�L| R �).
T (σ ∪ [x � → t]) ⊆ T (�L| R �).
If L ift(S) returns Unsat on G, then there is a set of guarded clauses R that have been produced by the deduction rules such that Proof. We define W to be the result of the Skolemization and the clausification of Ax. If W is feasible then so is Ax. As a consequence, since Ax is complete, every set of literals L such that

�(G ∪W ) • ∅ ∪ R� is T -unsatisfiable. By contradiction, assume that G is T � -satisfiable. Then there is a subset G � of literals of G such that G � is T � - satisfiable and G � � T G. Since Ax is sound, there is a model L of W ∪ G � . Consider L � = L ∪ {�ϕ • σ � | ϕ •σ ∈ (G∪W )•∅∪R and L � ϕσ }∪{¬ �ϕ • σ � | ϕ •σ ∈ (G∪W )•∅∪R and L � � ϕσ }. Since L is complete, for every closure l • σ , if L � � lσ then L � T lσ and, for every closure ϕ • σ and every substitution σ � such that L � T σ ≈ σ � , if L � T ϕσ then L � T ϕσ � . Therefore, L � is satisfiable. By construction, L � � T �(G ∪W ) • ∅�. By Lemma 3.1, we also have L � � T �R�. Since �(G ∪W ) • ∅ ∪ R� is unsatisfiable, we reach a contradiction.
W ∪ L is feasible is T � -satisfiable.
We say that a world L propositionally satisfies a set of ground clauses G whenever, for every clause C in G, there is a literal l ∈ C such that l ∈ L.

If L ift(S) returns Sat on G, then there is a set of guarded clauses R that have been produced by the deduction rules and a set of literals L that propositionally satisfies �R� and such that nothing more can be deduced from L| R . By definition of L| R , for every guarded clause

H → C ∈ R such that H ⊆ L| R , the set of ground literals �L| R � propositionally satisfies �C�. In particular, it propositionally satisfies �(G ∪W ) • ∅�. Consider the set of literals �L| R �. We show that �L| R � � W ∪ G. Since Ax is complete and L| R � � W ∪ L| R �, �L| R � is T � -satisfiable. Since �L| R � � T G so is G.
We show that, for every closure ϕ

• σ ∈ L| R , �L| R � � ϕσ by structural induction over ϕ. Since �L| R � propositionally satisfies �(G ∪W ) • ∅�, it is enough to conclude. • l • σ ∈ L| R . By definition of �L| R �, we have lσ ∈ �L| R �. • �l�C • σ ∈ L| R . Since nothing new can be deduced from L, then the two guarded clauses �l�C • σ → l • σ and �l�C • σ → C • σ are in R. Since �l�C • σ ∈ L| R , �L| R � satisfies the encoding of these two formulas. Thus, l • σ ∈ L| R and there is ϕ ∈ C such that ϕ • σ ∈ L| R . By induction hypothesis, �L| R � � lσ and �L| R � � ϕ • σ , and, therefore, �L| R � � �l�Cσ . • [l]C • σ ∈ L| R . If �L| R � � lσ , since nothing new can be deduced from L, there is U ⊆ L| R such that [l]C • σ ∧ � U → C • σ ∈ R. Since [l]C • σ ∈ L| R and U ⊆ L| R , �L| R � satisfies the encoding of this formula. Thus, there is ϕ ∈ C such that ϕ • σ ∈ L| R and, by induction hypothesis, �L| R � � ϕσ . As a consequence, �L| R � � [l]Cσ . • ∀x.C • σ ∈ L| R . Let t be a term of T (�L| R �). Since nothing new can be deduced from L, we have �L| R � ∪ {B ϕ σ | ϕ • σ ∈ L| R } ∪ {¬B ϕ (σ ∪ [x � → t]) | ϕ ∈ C} � T ⊥. Thus, there is ϕ ∈ C and a substitution µ such that ϕ • µ ∈ L| R and �L| R � � T µ ≈ σ ∪ [x ← t]. What is more, by Lemma 3.2, T (σ ) ⊆ T (�L| R �). Thus, by immediate induction over ϕ, �L| R � � ϕ(σ ∪ [x � → t]) and �L| R � � C(σ ∪ [x � → t]). Therefore, �L| R � � ∀x.Cσ .

Motivating Example

To ensure termination, on a weakly terminating axiomatization, we need to ensure fairness in the choice of instances. Unfortunately, this cannot be done easily. We show on an example that restricting instantiation based on a notion of instantiation level is not enough to ensure fairness.

We define the instantiation level of a formula to be the minimal number of instances needed to obtain the term used for the instance. We consider the following axiomatization W :

W = � ∀x.c ≈ c, ∀x.¬P(x), ∀x.[x ≈ c]P( f (x)), ∀x.Q( f (x)), ∀x.x ≈ c ∨ � �
The axiomatization W is weakly terminating. Indeed, the first line can produce an unsatisfiable set of literals in every truth assignment. It is enough to instantiate the first formula with any known term ω to produce the term c, then to use c to instantiate the third formula and get P( f (c)), and finally to instantiate the second formula with f (c) and thus obtain ¬P( f (c)).

The second line of axioms is designed to induce L ift(S) to loop on this example. The first formula, ∀x.Q( f (x)), has the potential to create an infinite sequence of new terms f (ω), f ( f (ω))... The last formula, ∀x.x ≈ c ∨ �, can prevent the instantiation of ∀x.[x ≈ c]P( f (x)) with c by equating it with a previously available term.

The solver L ift(S) can run forever on W ∪{ω ≈ ω}, and so, even if we require that instances are only done if there is no other possible instance of a smaller instantiation level and that all the possible instances of the smallest instantiation level are done at once.

L ift(S) launches the ground solver S on �R

0 � = �(W ∪ {ω ≈ ω}) • ∅�. It returns Sat(L 0 )
with L 0 = �R 0 �. The rules NEG UNFOLD and POS UNFOLD cannot be applied. Thus, L ift(S) generates every possible instance of level 1 in L 0 | R 0 :

R 1 = R 0 ∪                ∀x.c ≈ c • ∅ ∧ ω ≈ ω • ∅ → c ≈ c • ∅, ∀x.¬P(x) • ∅ ∧ ω ≈ ω • ∅ → ¬P(x) • [x � → ω], ∀x.[x ≈ c]P( f (x)) • ∅ ∧ ω ≈ ω • ∅ → [x ≈ c]P( f (x)) • [x � → ω], ∀x.Q( f (x)) • ∅ ∧ ω ≈ ω • ∅ → Q( f (x)) • [x � → ω], ∀x.x ≈ c ∨ � • ∅ ∧ ω ≈ ω • ∅ → x ≈ c ∨ � • [x � → ω]                2. The ground solver S returns Sat(L 1 ) on �R 1 � with L 1 such that L 1 | R 1 contains x ≈ c • [x � → ω]. L ift(S) applies NEG UNFOLD to unfold the trigger on [x ≈ c] P( f (x)) • [x � → ω]: R 2 = R 1 ∪ {[x ≈ c] P( f (x)) • [x � → ω] ∧ x ≈ c • [x � → ω] → P( f (x)) • [x � → ω]} 3. The ground solver S returns Sat(L 2 ) on �R 2 � with L 2 = L 1 ∪ P( f (ω)). Since �L 2 | R 2 � � T ω ≈ c, every instance with the term c is redundant. L ift(S) generates every possible instance of level 2 in L 2 | R 2 : R 3 = R 2 ∪          ∀x.¬P(x) • ∅ ∧ Q( f (x)) • [x � → ω] → ¬P(x) • [x � → f (ω)], ∀x.[x ≈ c]P( f (x)) • ∅ ∧ Q( f (x)) • [x � → ω] → [x ≈ c]P( f (x)) • [x � → f (ω)], ∀x.Q( f (x)) • ∅ ∧ Q( f (x)) • [x � → ω] → Q( f (x)) • [x � → f (ω)], ∀x.x ≈ c ∨ � • ∅ ∧ Q( f (x)) • [x � → ω] → x ≈ c ∨ � • [x � → f (ω)]          4. The ground solver S returns Sat(L 3 ) on �R 3 � with L 3 such that L 3 | R 3 does not contain x ≈ c • [x � → ω] nor P( f (x)) • [x � → ω] anymore, but contains x ≈ c • [x � → f (ω)]. Like in step 2, L ift(S) applies NEG UNFOLD: R 4 = R 3 ∪ {[x ≈ c] P( f (x)) • [x � → f (ω)] ∧ x ≈ c • [x � → ω] → P( f (x)) • [x � → f (ω)]} 5. The ground solver S returns Sat(L 4 ) on �R 4 � with L 4 = L 3 ∪P( f ( f (ω))). Since �L 4 | R 4 � � T f (ω) ≈ c
, every instance with the term c is still redundant. We can generate every possible instance of level 3.

During this run, the solver L ift(S) will instantiate the third axiom with the terms ω, f (ω), f ( f (ω))... and never with c that would have allowed it to terminate.

Termination

As we have seen on the preceding example, enforcing fairness in application of INST is not enough to ensure the termination of L ift(S) on a weakly terminating axiomatization. Thus, we concentrate here on strongly terminating axiomatizations:

Theorem 3.3 (Termination).
Let W be the Skolemization of an axiomatization Ax. If W is strongly terminating then the solver L ift(S) will terminate on any set of ground clauses G along with the axiomatization W .

Proof. Let R be a set of guarded clauses deduced from (W ∪ G) • ∅. Let M be a set of closures.

We define the set of available theory clauses of R from M, written V M , to be the set of theory clauses {C | H → C ∈ R and, for every ϕ

• σ ∈ H, ϕ • σ ∈ M}.
We say that a truth assignment A is coherent if, for every closure ϕ • σ ∈ A, for every theory clause C ∈ A, and for every closure ϕ

• σ � ∈ C, if �A� � T σ ≈ σ � then ϕ • σ � ∈ A.
We define a truth assignment of R to be the limit of the sequence A i of truth assignments such that A 0 = ∅ and A n+1 is a truth assignment of V A n ∪ A n . Such a limit exists since A n ⊆ A n+1 and there is only a finite number of truth assignments of R since A only contains sub-formulas of elements of R.

We then consider the maximal depth d A of any instantiation tree of A for every truth assignment A of R. We call S R the multi-set {d A | A is a coherent truth assignment of R}. Theoretically, S R may contain ∞ if there is a coherent truth assignment A of R that has an infinite instantiation tree. In practice, we start with a set of natural numbers since, by definition of strong termination, every truth assignment of (W ∪ G) • ∅ only admits finite instantiation trees and then we show that S R decreases throughout the derivation according to the standard order on multisets.

Let V be a set of theory clauses and let C • σ be a theory clause and H ⊆ V be a set of closures such that H → C • σ can appear as the conclusion of POS UNFOLD, NEG UNFOLD, or INST. We show that {d

A | A is a coherent truth assignment of V ∪C • σ } is smaller than {d A | A is a coherent truth assignment of V }.
If the clause H → C • σ can be deduced by POS UNFOLD or NEG UNFOLD then the truth assignments of V ∪C • σ are exactly the truth assignments of V . Indeed, since the guards H are in V , the rules of Definition 2.10 are applicable. Thus {d

A | A is a coherent truth assignment of V ∪C • σ } is equal to {d A | A is a coherent truth assignment of V }.
If the clause H → C • σ can be deduced by INST then there is exactly one coherent truth assignment

A � of A ∪C • σ per coherent truth assignment A of V such that C • σ is redundant in A for which we have d A = d A � plus a bunch of coherent truth assignments A � of A ∪C • σ for each coherent truth assignment A of V such that C • σ is not redundant in A.
For these additional truth assignments, we have d A > d A � . Indeed every instantiation tree for A � can be inserted in a strictly greater instantiation tree for A that starts with the instance C

• σ . Thus, {d A | A is a coherent truth assignment of V ∪ C • σ } is smaller or equal to {d A | A is a coherent truth assignment of V }. What is more, if there is a truth assignment A of V such that C • σ is not redundant in A then {d A | A is a coherent truth assignment of V ∪C • σ } is strictly smaller than {d A | A is a coherent truth assignment of V }.
Let us consider a deduction step from R to R � and let us compare S R and S R � . The set of coherent truth assignments of R � can be constructed by taking every truth assignment A of R and then adding iteratively the clauses of the set of available clauses of R � from A. As we have just shown, this can only decrease S R .

What is more, if the deduction step from R to R � involves INST steps then no other deduction rule can be applied on L| R . Thus L| R ∪V L| R is a truth assignment of R. Moreover, it is coherent since, by hypothesis on the ground solver S, the model L returned by S in Sat(L) contains every literal of �R� that is implied by

L. Since �L| R � ∪ {B ϕ σ | ϕ • σ ∈ L| R } � T � ϕ∈C B ϕ (σ ∪ [x � → t]), the theory clause C • (σ ∪ [x � → t]) is not redundant in L| R ∪V L| R . Indeed, since L propositionally satisfies �R�, L| R propositionally satisfies V L| R and, if C •(σ ∪[x � → t]) was redundant in L| R ∪V L| R , � ϕ∈C B ϕ (σ ∪ [x � → t]) would be satisfied by �L| R � ∪ {B ϕ σ | ϕ • σ ∈ L| R }. Thus, S R � must be strictly smaller than S R .
Thus, if L ift(S) can run forever on W ∪ G then there is an infinite sequence of applications of POS UNFOLD and NEG UNFOLD on a finite set of clauses R. It is impossible because these three rules only deduce sub-formulas of R.

Implementation

The implementation was done in OCaml using the OCaml API of Z3 3.2. It represents approximatively 1800 lines of code.

Description

The solver's interface uses a simplified version of SMT-LIB v2 syntax. Sorts as well as functions can be declared. Booleans and integers are built-in, along with some usual operations on them (conjunction, disjunction, and negation for booleans and addition, subtraction, multiplication, and comparison for integers). Boolean formulas may also contain universally quantified formulas with triggers with a positive polarity, that is, universal quantifiers that appear under an even number of negations. Existentially quantified formulas (or universally quantified formulas with a negative polarity) have to be Skolemized. Once a set of boolean formulas has been asserted, the solver L ift(S) can be launched. It either returns Sat or Unsat.

The core of the solver is made of two parts. The first part, that we call the wrapper, is in charge of interacting with the ground solver S (here Z3 3.2) using the SMT-LIB v2 syntax. The second part, called the generator, applies the deduction rules.

The wrapper uses an instance of Z3. It contains the set of ground formulas that is iteratively incremented by the generator. It is also used to assume the model returned by the first solver and use it during the application of the deduction rules. The wrapper takes care of the encoding of pseudo-literals into opaque literals. It also translates back the relevant closures of the ground models.

The generator repeatedly uses the wrapper to check the validity of the current set R of ground formulas. If the wrapper returns Sat with a model L, it goes through the relevant closures of L to apply the deduction rules. For every trigger [l]C • σ , no matter if it has already been unfolded before, the generator checks whether �L| R � � lσ . Indeed, it is faster than going through the previous deductions

�[l]C • σ ∧ � M � → C • σ � of NEG UNFOLD to check whether M � ⊆ L| R .
In the same way, for every universally quantified formula ∀x.C • σ ∈ L| R and every term t ∈ T (L| R ), we check whether

�L| R � ∪ �L| R � � T C • σ whenever there is l • σ � ∈ L| R such that t ∈ T (l • σ � ) and the ground formula �∀x.C • σ ∧ l • σ � → C • (σ ∪ [x � → t])� has not been deduced yet.

Benchmarks

We test our implementation on several examples using either the theory of arrays or our theory of imperative doubly-linked lists.

On the theory of arrays, we use two families of tests parametrized by a natural n. The first family, named scoped _ updates, expresses that the value stored in an array at a position j is preserved through n updates at positions different from j:

∀a, i 1 , . . . , i n , j, v. (i 1 � ≈ j ∧ • • • ∧ i n � ≈ j) → get(set(. . . set(a, i 1 , v) . . . , i n , v), j) ≈ get(a, j)
The second family, named equal _ updates, expresses that the values stored at an index j in a 1 and a n+1 are equal if there are indexes i 1 ...i n different from j such that a k updated at the index i k is a k+1 updated at the index i k : ∀a 1 , . . . , a n+1 , i 1 , . . . , i n , j, v.

(i 1 � ≈ j ∧ • • • ∧ i n � ≈ j ∧ set(a 1 , i 1 , v) ≈ set(a 2 , i 1 , v) ∧ • • • ∧ set(a n , i n , v) ≈ set(a n+1 , i n , v)) → get(a 1 , j) ≈ get(a n+1 , j)
The results of these tests are presented in Figure 3.2. The errors come from the fact that Z3 stops after some time returning Unknown. On the theory of list, we have launched a specific test and two families. The specific test, called replace, replaces an element in the list and then checks that the elements and the cursors are as expected: ∀co 1 , co 2 : list, cu 1 , cu 2 : cursor, e : element_type.

(has_element(co 1 , cu 1 ) ≈ t ∧ has_element(co 1 , cu 2 ) ≈ t ∧ cu 1 � ≈ c 2 ∧ replace_element(co 1 , cu 1 , e, co 2 ) ≈ t) → position(co 2 , cu 1 ) ≈ position(co 1 , cu 1 ) ∧ position(co 2 , cu 2 ) ≈ position(co 1 , cu 2 ) ∧ element(co 2 , cu 1 ) ≈ e ∧ element(co 2 , cu 2 ) ≈ element(co 1 , cu 2 )
The first test family, called delete _ length, deletes n cursors in a list and checks that the length is decreased by n: ∀co 0 , co 1 , . . . , co n : list, cu 1 , . . . , cu n : cursor.

(has_element(co 0 , cu 1 ) ≈ t ∧ • • • ∧ has_element(co 0 , cu n ) ≈ t ∧ � 1≤i< j≤n cu i � ≈ cu j ∧ delete(co 0 , cu 1 , co 1 ) ≈ t ∧ • • • ∧ delete(co n-1 , cu n , co n ) ≈ t) → length(co 0 ) ≈ length(co n ) + n
The second test family, called delete _ last, inserts an element at the beginning of a list of length 4 and then deletes the n last elements (n < 4). It checks that the inserted cursor is preserved: ∀co 0 , co 1 , . . . , co n+1 : list, e : element_type.

(length(co 0 ) ≈ 4 ∧ insert(co 0 , f irst(co 0 ), e, co 1 ) ≈ t ∧ delete(co 1 , last(co 1 ), co 2 ) ∧ • • • ∧ delete(co n , last(co n ), co n+1 ) → has_element(co n+1 , f irst(co 1 ))
The results of these tests are presented in Figure 3.3.

Like previously, error denote the fact that Z3 stops after some time returning Unknown.

All these tests should be trivial (Z3 solves all of them in less than 0.1 second). The black-box approach gives poor benchmarks because:

• We need to call the solver S on every universally quantified formula in L| R and every term of T (�L| R �) for which we want to do an instance and every trigger in L| R that we want to unfold.

• We do not use E-matching instantiation techniques so we generate an instance per couple of a term and a universally quantified formula (and maybe more if there are several literals with the same term). • Instances and unfolding of triggers have to be done several times because they need to be prefixed by the literals that have caused the unfolding.

• The ground solver S has to handle a great deal of formulas as well as numerous push and pop operations in the environment.

Conclusion

This implementation that relies on a black-box SMT solver conforms with our framework as it yields a decision procedure for every sound, complete, and strongly terminating axiomatization of a theory T � . Still, we have seen that it is not usable in practice as it requires too costly communication with the underlying SMT solver. Alleviating this problem requires a tighter integration of the handling of first-order axioms into an SMT solver's main loop. This is the subject of the next chapter. In Chapter 2, we have introduced a semantics for first-order logic with triggers as well of some notions of soundness, completeness, and termination in this logic. We have presented in Chapter 3 a theoretical way to extend a generic ground SMT solver to our logic. We have seen that its implementation results in poor performances so that this impementation does not give a pratical way of extending a ground SMT solver with a new theory.

In this chapter, we try to come up with a more efficient implementation by tightly integrating our mechanism with the solver's ground reasoning. We choose to extend the well-known Abstract DPLL Modulo Theory framework, on which most of the state of the art SMT solvers are based. We then demonstrate that our version of DPLL can effectively decide the satisfiability of ground formulas in an extension of the solver's background theory, whenever this extension is defined by a sound, complete, and terminating axiomatization. In Section 4.2, we present an implementation of our framework inside the first-order SMT solver Alt-Ergo. We use it to show that, for our example theory of doubly-linked lists described in Section 2.2, not only we obtain completeness and termination, but the eager instantiation allowed by the termination of quantifier handling on this axiomatization results in improved overall performance of the prover.

Description

In this section, we introduce an extension of abstract DPLL modulo theories [START_REF] Nieuwenhuis | Solving SAT and SAT modulo theories: From an abstract Davis-Putnam-Logemann-Loveland procedure to DPLL(T)[END_REF] that handles formulas with triggers and witnesses. We show that if a set of axioms is sound and complete with respect to a theory T � which extends the solver's background theory T , then our procedure is sound and complete on any ground satisfiability problem in T � . Moreover, we show that under certain fairness restrictions on derivations, our procedure terminates on any ground satisfiability problem if the axiomatization is terminating. For all this section, the background theory T is fixed.

Preliminaries

We describe a solver that takes a set of first-order axioms with triggers and witnesses, denoted Ax, and a set of ground clauses, denoted G. Before starting the DPLL procedure, we Skolemize and clausify the axioms in Ax, producing a set of pseudo-clauses W , as described in Section 2.1.4. Then we convert W into a set of theory clauses (disjunctions of closures) by coupling it with the empty substitution: W • ∅. We run the procedure on W • ∅ and G, with one of the three possible outcomes:

• the solver returns Unsat, meaning that the union Ax ∪ G is unsatisfiable-therefore, if Ax is sound with respect to T � , set G is T � -unsatisfiable;

• the solver returns Sat, meaning that there exists a ground formula G � such that G � � T G and the union Ax ∪ G � is feasible-therefore, if Ax is complete with respect to T � , then G � is T � -satisfiable, and consequently, G is T � -satisfiable;

• the solver runs indefinitely-if W is terminating, this cannot happen.

When we do not have the soundness and completeness properties for Ax, the union Ax ∪ G may be both feasible (true in some world) and unsatisfiable (false in every complete world). In this case, the solver is nondeterministic. For example, let Ax be the single axiom [a] ⊥ and G the single clause a ≈ a ∨ �. Then the solver may drop � from G, learn constant a, remove the trigger and let the contradiction out, producing Unsat. Alternatively, the solver may discard the whole clause G as redundant and return Sat: the union Ax ∪ G is true in the empty world. Note the slightly complicated explanation of the Sat case: instead of finding a world directly for Ax ∪ G, the solver only ensures the feasibility of Ax joined with some ground antecedent of G modulo T , which is not at all guaranteed to contain the same terms and to behave the same as G with respect to the � T relation. This is an important feature of our approach: the input problem G is considered modulo theory T and the solver is free to make simplifications as long as they are permitted by T , without regard to known and unknown terms. In that way, we stay consistent with the traditional semantics of DPLL. On the other hand, axiomatization Ax is treated according to the semantics in Section 2.1.2.

To maintain this distinction, the solver works with two distinct kinds of clauses. The clauses coming from Ax are theory clauses: disjunctions of closures that accumulate ground substitutions into free variables. The clauses coming from G are the usual disjunctions of ground literals; we call them user clauses to distinguish them from the clauses of the first kind. The empty clause ⊥ is considered to be a user clause. A super-clause is either a theory clause or a user clause.

Besides the current set of clauses (which can be modified by learning and forgetting), DPLLbased procedures maintain a set of currently assumed facts. In our procedure, these facts, which we collectively call super-literals, may be of three different kinds:

• a literal l; • a closure ϕ • σ ; • an anti-closure ¬(ϕ • σ ).
The latter kind appears when we backtrack a decision step over a closure. We extend the T operation (set of subterms) to closures and anti-closures as follows:

T (l • σ ) � T (lσ ) T (ϕ • σ ) � T (σ ) if ϕ is not a literal T (¬(ϕ • σ )) � ∅ Non-literal closures ϕ • σ
, where ϕ is a formula under a trigger, a witness, or a universal quantifier, are treated as opaque boxes so that the only terms we can learn from them are the ones brought by substitution σ . An anti-closure ¬(ϕ • σ ) does not give us any new terms at all (and thus should not be confused with (¬ϕ) • σ ). Indeed, if the solver at some moment decides to assume a given closure and later reverts this decision, it should not retain the terms learned from that closure. Given a set of super-literals M, we define LIT(M) to be the set of literals in M, and CLO(M) to be the set of closures in M. Given a set of super-clauses F, we define LIT(F) to be the set of unit user clauses in F, and CLO(F) to be the set of unit theory clauses in F.

To model the trigger mechanism, we need a way to protect a super-clause so that its elements are not available until a certain condition is fulfilled. We define a guarded clause as a pair H → C, where the guard H is a conjunctive set of closures and C is a super-clause. If M is a set of superliterals and F a set of guarded clauses, we define the set of available super-clauses to be the set of super-clauses of F whose guard is directly in M:

AVB(F, M) � LIT(M) ∪ CLO(M) ∪ {C | H → C ∈ F and H ⊆ M}
Any more complex reasoning on guards is left to DPLL. We also use the set of guards of F, defined as GRD(F)

� {H | H → C ∈ F}.
We now extend Definitions 2.4 and 2.5 onto super-literals and guarded clauses. super-clause, a guard, or a guarded clause to be true is L, written L � T F, as follows:

L � T l L � T l L � T ϕ • σ L ∪ known(T (L)) � T known(T (σ )) and L � T ϕσ L � T ¬(ϕ • σ ) if L ∪ known(T (L)) � T known(T (σ )) then L � � T ϕσ L � T C C is a user clause and L � T C L � T C
C is a theory clause and for some ϕ

• σ ∈ C, L � T ϕ • σ L � T H H is a guard and for each ϕ • σ ∈ H, L � T ϕ • σ L � T H → C if L � T H then L � T C
We say that a super-literal is false in L when its negation is true in L. We call a super-literal, a super-clause, a guard, or a guarded clause feasible if there exists a world in which it is true. We call a super-literal, a super-clause, a guard, or a guarded clause satisfiable if there exists a complete world-which we then call its model-in which it is true.

On normal literals (not closures) and user clauses, � T coincides with � T : a user clause C is true in a world L if and only if it is true in every model of L. On closures and theory clauses, � T refers to � T : a theory clause is true in L if and only if one of its closures is true in L. By a slight abuse of terminology, we reuse the terms of Definitions 2.4 and 2.5, even though they have different meanings for ordinary literals; in this section, we follow Definition 4.1.

We define a version of implication that treats closures as opaque "atoms" whose arguments are given by the accumulated substitution. This is the implication used in the DPLL solver, the semantics of closures being taken care of by specific additional rules. Definition 4.2. We define an encoding � � of super-literals and guarded clauses into literals and clauses. In the rules below, P ϕ is a fresh predicate symbol that we associate to every pseudoliteral ϕ. The arity of P ϕ is the number of free variables in ϕ.

�l� � l �l • σ � � lσ �ϕ • σ � � P ϕ (vars(ϕ))σ if ϕ is not a literal �¬(ϕ • σ )� � ¬ �ϕ • σ � �e 1 ∨ • • • ∨ e m � � �e 1 � ∨ • • • ∨ �e m � �(g 1 ∧ • • • ∧ g n ) → (e 1 ∨ • • • ∨ e m )� � ¬ �g 1 � ∨ • • • ∨ ¬ �g n � ∨ �e 1 � ∨ • • • ∨ �e m �
Let S be a conjunctive set of super-literals and/or guarded clauses. Let E be a super-literal, a super-clause, or a guarded clause. We define S � � T E to be �S� � T �E�.

� �
T is a conservative extension of the usual first-order implication � T onto super-literals and guarded clauses. More generally, we have: Proof. Let L be a model of S. We define L � = L ∪ {�e� | e is a super-literal such that L � T e}. The set L � is satisfiable and complete. Indeed, for every closure ϕ • σ and every substitution σ

� such that L � T σ ≈ σ � , L � T ϕ • σ if and only if L � � T ¬(ϕ • σ � ) and L � T ¬ϕ • σ if and only if L � � T ϕ • σ � .
We show that L � � T �S�. Since L � T S, for every super-literal e in S, �e� ∈ L � . Let H → C be a guarded clause of S. If L � � T H then there is e ∈ H such that L � T ¬e. By construction, �¬e� ∈ L � and L � � T �H → C�. Otherwise, there is e ∈ C such that L � T e, �e� ∈ L � and L � � T �H → C�.

Since S � � T E, L � is a model of �E�. Thus, if E is a super-literal then �E� ∈ L � and, by construction of

L � , L � T E. If E is a guarded clause (g 1 ∧ • • • ∧ g n ) → (e 1 ∨ • • • ∨ e m ) then there is e ∈ {¬g 1 . . . ¬g n , e 1 . . . e m } such that L � T e and therefore either L � � T g 1 ∧ • • • ∧ g n or L � T e 1 ∨ • • • ∨ e m .
The case where E is a super-clause is handled in the same way.

We also need a weaker version of implication that preserves feasibility as well as satisfiability. We want this implication to be as close as possible to the usual implication � T on user clauses while remaining computable by a working solver on theory clauses. We use the set �V � of ground literals readily available from a set of theory clauses V as defined in Definition 2.9: Definition 4.3. Let F be a set of super-clauses and C a super-clause. We write F � � T C if and only if one of the following conditions holds:

• C is a unit user clause and LIT(F) ∪ �CLO(F)� � T C; • C is a non-unit user clause and {C � | C � is a user clause of F} ∪ �CLO(F)� � T C; • C is a theory clause D • σ and there is l ∈ D such that F ∪ known(T (CLO(F))) � � T known(T (lσ )) and F � � T lσ ;
• C is a theory clause D • σ and there is a theory clause

C � • σ � ∈ F such that C � ⊆ D, F � � T σ | Dom(σ � ) ≈ σ � , and F ∪ known(T (CLO(F))) � � T known(T (σ | Dom(σ � ) )).

Remark that � �

T does not coincide with implication modulo T on unit user clauses. Indeed,

� �
T is used in particular to decide that a clause is unnecessary for the proof and therefore can be forgotten or not generated. In the definition of truth assignment, we state that the solver should assume unit clauses eagerly while it is allowed to postpone deciding on the literals of non-unit clauses. Thus, even if a set of non-unit clauses implies a unit clause C, the solver cannot be allowed to forget C without compromising termination. For example, consider the set of axioms:

F = {c ≈ c, f (c) ≈ f (c), f (c) ≈ c, ∀x[ f (c) ≈ c]. f (x) ≈ x, ∀x. f (x) ≈ f (x)}
The set F is terminating (every term introduced by the last axiom can be equated to an already known term by the previous one). Still, consider the set

G = { f (c) ≈ c, f (c) ≈ c ∨ c � ≈ c}. We have F \ { f (c) ≈ c} • ∅ ∪ G � � T f (c) ≈ c • ∅, and thus f (c) ≈ c can be removed from F. We have f (c) ≈ c ∨ c � ≈ c � T f (c) ≈ c. Assume we can remove f (c) ≈ c from G.
Then, the solver can produce an infinite number of terms from F \ { f (c) ≈ c}. It may never choose to deduce f (c) ≈ c from f (c) ≈ c ∨ c � ≈ c which would allow all these terms to collapse.

In the last two cases of Definition 4.3, known terms are only provided by the closures (that is, unit theory clauses) of F and not by the user clauses. Indeed, as we said earlier, we treat user clauses according to the usual first-order semantics, where a literal may be replaced by an equivalent one regardless of its subterms. Otherwise, C is a theory clause D • σ . Assume that there is l ∈ D such that F � � T lσ and

F ∪ known(T (CLO(F))) � � T known(T (lσ )). Since L � F, L � T LIT(F) ∪ �CLO(F)� and L ∪ known(T (L)) � T known(T (CLO(F))). As a consequence, L � l • σ | vars(l) and L � C.
Otherwise, there is a theory clause

C � • σ � ∈ F such that C � ⊆ D, F � � T σ | Dom(σ � ) ≈ σ � , and F ∪ known(T (CLO(F))) � � T known(T (σ | Dom(σ � ) )). Since L � F, L � T σ | Dom(σ � ) ≈ σ � and L ∪ known(T (L)) � T known(T (σ | Dom(σ � ) )
), as per the previous case. Furthermore, there is an element ϕ

• σ � | vars(ϕ) of C � • σ � such that L � ϕ • σ � | vars(ϕ)
and thus L � ϕσ � . Since every term substituted by σ into a free variable of ϕ is known from L, and since σ and σ � substitute the same terms modulo L and T into every free variable of ϕ, we have L � ϕσ . As a consequence, Otherwise, there is a theory clause

L � ϕ • σ | vars(ϕ) and L � C.
C � • σ � ∈ F such that C � ⊆ D, F � � T σ | Dom(σ � ) ≈ σ � , and F ∪ known(T (CLO(F))) � � T known(T (σ | Dom(σ � ) )). Since L � T �F�, L � T σ | Dom(σ � ) ≈ σ � as per the previous case. Since L � T �C � • σ � �, we have L � T �C � • σ �. Thus, L � T �D • σ �.
. If F 1 � � T F 2 and F 2 � � T C, then F 1 � � T C. Proof. Assume that C is a unit user clause l and LIT(F 2 ) ∪ �CLO(F 2 )� � T C. Since F 1 � � T F 2 , we have F 1 � � T LIT(F 2 ) and F 1 � � T �CLO(F 2 )�. By definition of � � T on user clauses, LIT(F 1 ) ∪ �CLO(F 1 )� � T LIT(F 2 ) ∪ �CLO(F 2 )�. Thus, F 1 � � T C.
The case where C is a non-unit user clause is handled in the same way, except that instead of LIT(F 1 ) and LIT(F 2 ) we consider the sets of all user clauses in F 1 and F 2 , respectively.

Otherwise, C is a theory clause D • σ . Assume that there is a literal l ∈ D such that F 2 � � T lσ and F 2 ∪ known(T (CLO(F 2 ))) � � T known(T (lσ )). Like in the previous case,

F 1 � � T lσ . Since F 1 � � T F 2 , F 1 ∪ known(T (CLO(F 1 ))) � � T known(T (ϕ • σ )) for every closure ϕ • σ ∈ CLO(F 2 ). As a consequence, LIT(F 1 ) ∪ �CLO(F 1 )� ∪ known(T (CLO(F 1 ))) � T LIT(F 2 ) ∪ �CLO(F 2 )� ∪ known(T (CLO(F 2 ))) and F 1 � � T C. Otherwise, there is a theory clause C � • σ � ∈ F 2 such that C � ⊆ D, F 2 � � T σ | Dom(σ � ) ≈ σ � , and F 2 ∪ known(T (CLO(F 2 ))) � � T known(T (σ | Dom(σ � ) )
). Like in the previous case, we have

F 1 � � T σ | Dom(σ � ) ≈ σ � and F 1 ∪ known(T (CLO(F 1 ))) � � T known(T (σ | Dom(σ � ) )). Assume that there is a literal l ∈ C � such that F 1 � � T lσ � and F 1 ∪ known(T (CLO(F 1 ))) � � T known(T (lσ � )). Since F 1 � � T σ | Dom(σ � ) ≈ σ � , F 1 � � T lσ . With F 1 ∪ known(T (CLO(F 1 ))) � � T known(T (σ | Dom(σ � ) )), we deduce F 1 ∪ known(T (CLO(F 1 ))) � � T known(T (lσ )). Therefore, F 1 � � T C.
Otherwise, there is a theory clause

C �� • σ �� ∈ F 1 such that C �� ⊆ C � , F 1 � � T σ � | Dom(σ �� ) ≈ σ �� , and F 1 ∪ known(T (CLO(F 1 ))) � � T known(T (σ � | Dom(σ �� ) )). Since F 1 � � T σ | Dom(σ � ) ≈ σ � , we have F 1 � � T σ | Dom(σ �� ) ≈ σ �� . Hence, F 1 � � T C.
Given a set of super-literals M, we write M � � T C as an abbreviation for LIT(M)∪ CLO(M) � � T C. In other words, we treat literals and closures in M as unit user clauses and theory clauses, respectively, and we ignore the anti-closures. According to this definition, M � � T ⊥ whenever the set LIT(M) ∪ �CLO(M)� is unsatisfiable.

In our algorithm, we use terms coming from the user clauses to instantiate universally quantified formulas and to unfold triggers. To make these terms usable for the � � T relation, we need to convert the literals in the set of assumed facts to closures, as follows. Given a set of superliterals M, we define �M� to be M ∪ {l • ∅ | l ∈ LIT(M)}. Thus, for every term t ∈ T (M), t ∈ T (CLO(�M�)). Proof. If L is a model of �M� then L is also a model of ��M��.

Description of DPLL(T) with triggers

The method introduced below adapts the principles of abstract DPLL modulo theories (following [START_REF] Nieuwenhuis | Solving SAT and SAT modulo theories: From an abstract Davis-Putnam-Logemann-Loveland procedure to DPLL(T)[END_REF]) to super-literals and guarded clauses. We call this adaptation DPLL � (T ) or DPLL � since T is fixed.

Deduction Rules of DPLL � (T)

The rules are given in Figures 4.1 and 4.2. They attempt to construct a model of a set of guarded clauses F. The partial model is represented as a set of super-literals M that are assumed to be true. We call state of the procedure the pair M � F and we say that a super-literal e is defined in M if either e or ¬e is in M.

The elements of an available clause can be given an arbitrary truth value using the rule

Decide. Super-literals of M whose truth value was chosen arbitrarily are labeled with a letter d

M � F, H → C ∨ e =⇒ Me � F, H → C ∨ e if � H ∧ ¬C ⊆ M e is undefined in M Decide: M � F =⇒ Me d � F if � e or ¬e occurs in AVB(F, M) e is undefined in M Fail: M � F, H → C =⇒ fail if � H ∧ ¬C ⊆ M M contains no

decision literals

Restart:

M � F =⇒ ∅ � F T -Propagate: M � F =⇒ Me � F if      e / ∈ M
M � F =⇒ M � F, H → C if      every atom of H occurs in GRD(F) ∪ �M� every atom of C occurs in AVB(F, H) ∪ LIT(M) F, H � � T C T -Forget: M � F, H → C =⇒ M � F if � each closure of C defined in M occurs in AVB(F, H) AVB(F, H) � � T C T -Backjump: Me d N � F =⇒ Me � � F if                there is H → C ∈ F such that H ∧ ¬C ⊆ Me d N there is D ⊆ M such that: F, D � � T e � , e � is
M � F =⇒ M � F, (∀x.C • σ ) ∧ x ≈ x • [x � → t] → C • (σ ∪ [x � → t]) if      ∀x.C • σ is in M t ∈ T (M) AVB(F, M) � � � T C • (σ ∪ [x � → t])
Witness-Unfold: and called decision super-literals. If every element of a clause is false but one, the remaining element has to be true for the clause to be verified. It can be propagated using UnitPropagate.

M � F =⇒ M � F, �l�C • σ → l • σ , �l�C • σ → C • σ if � �l�C • σ is in M Trigger-Unfold: M � F =⇒ M � F, [l]C • σ ∧ l • σ → C • σ if � [l]C • σ is in M �M� � � T l • σ
If every element of an available clause is false then the corresponding guarded clause is called a conflict clause. If there is a conflict clause in F and there is no arbitrary choice in M, then a special state, named fail, can be reached through Fail. It means that no model could be found for F. The rule Restart can be used to restart the search from scratch. If there is a super-literal e that appears in available clauses or guards of F whose negation leads to a contradiction in M, it can be propagated using T -Propagate.

The set of guarded clauses F can be modified during the search using the rules T -Learn and T -Forget. Unlike the classical DPLL, we impose different conditions on the clauses that can be learned and the clauses that can be forgotten. We allow to learn any clause H → C if F, H � � T C, and thus every model of �F� is also a model of �H → C�. However, we are more restrictive with respect to what clauses can be forgotten. Namely, we require that for a guarded clause H → C to be forgotten, AVB(F, H) � � T C. We show below that this distinction is necessary for termination. Finally, if every element of an available clause of F is false and there is at least a decision literal in M, the rule T -Backjump can be applied. It allows to remove one or several decisions of M as long as there is a new element that can be added to M. An element can be added to M if it is implied by M and F.

Specific rules are needed to retrieve information from closures that are described in Fig. 4.2. The formulas added by these rules to the set of guarded clauses F are tautologies in the semantics of formulas with triggers. The rule Instantiate creates a new instance of a universally quantified formula of M with a sub-term of M. The rule Witness-Unfold handles a witness �l�C as a conjunction l ∧C. The rule Trigger-Unfold uses the guard mechanism to protect elements of trigger so that they cannot be decided upon or propagated until the guard is unfolded. An application of one of these three rules is said to be redundant in F, if the added guarded clauses are redundant in F, and a guarded clause H → C is said to be redundant in F if AVB(F, H) � � T C.

Termination Criteria of DPLL � (T)

A solver implementing DPLL � (T) attempts to construct a model of a set of guarded clauses by using the rules described in Figures 4.1 and 4.2 in an arbitrary way. We finally define when such a solver is allowed to stop, that is, to deduce the satisfiability or unsatisfiability of a set of ground clauses G modulo an extension of the background theory T described as an axiomatization W : • The loss of a unit clause that is implied by non-unit clauses. In the definition of the termination property, we only require that an element of a unit clause is added to truth assignments. Indeed, we do not want to ask for an application of Decide if there is another rule, for example, Instantiate, that can be applied. This motivates the constraints on T -Forget.

Property 4.1. The solver can return Unsat on G if ∅ � W • ∅ ∪ G =⇒ � _ � fail. Property 4.2. The solver can return Sat on G if ∅ � W • ∅ ∪ G =⇒ � M � F where: (i) M � � T AVB(F, M), (ii) M � � � T ⊥,

Termination Related Constraints

• The generation of an instance that is redundant as far as truth assignments are concerned. Indeed, the construction of instantiation trees stops as soon as a final truth assignment is reached. This motivates the constraints on Instantiate.

In the rule T -Propagate, we only allow e ∈ GRD(F) to be added to M if �M� � � T e. Indeed, a trigger [l]C • σ is supposed to protect elements of C until l is true in M and all its sub-terms are known in M. This is exactly what we get by requesting

�M� � � T lσ , namely LIT(M) ∪ {l � σ � | l � • σ � ∈ M} � T lσ and LIT(M) ∪ {l � σ � | l � • σ � ∈ M} ∪ known(T (M)) � T

known(T (lσ )). Only requesting that M � �

T lσ would not have been enough. For example, consider the axiomatization W 1 = {∀x.[ f (x)]p( f (x)) ≈ t}. We can easily check that W 1 is terminating. Indeed, every subterm of the form f (t � ) of every truth assignment of

[ f (x)]p( f (x)) ≈ t • [x � → t] ∪ L • ∅ is either a sub-term of L or a sub-term of t. Still, M � � T ( f (x) ≈ f (x)) • [x � → t] for every term t ∈ T (M).
As a consequence, for any term t in M, p( f

(x)) ≈ t • [x � → t] and then p( f (x)) ≈ t • [x � → f (t)], p( f (x)) ≈ t • [x � → f ( f (t))].
.. can be added to M.

In the rule T -Backjump, we require that e � or ¬e � occurs in AVB(F, M) ∪ LIT(Me d N). Assume that e � or ¬e � is allowed to appear in Me d N and consider the axiomatization:

W 2 = {∀y.[p(y) ≈ t]∀x. f (x, y) ≈ x, ∀y.[p(y) ≈ t]∀x. f (x, y) ≈ f (x, y), c ≈ c}
This axiomatization is terminating because as long as we have some p(t) ≈ t to generate new terms f (t � ,t) using the second axiom, we can also use the first axiom to collapse them to t � . Assume we launch the solver on a set of user clauses

G 2 = {p(a) ≈ t, p(a) � ≈ t ∨ p(b) ≈ t, p(c) ≈ t ∨ a ≈ a, p(a) � ≈ t ∨ a ≈ c}.
We can add p(a) ≈ t to M using UnitPropagate. We instantiate the first formula of W 2 with a ∈ T (M) and apply T -Propagate, UnitPropagate, and Trigger-Unfold so that (∀x. f (x, y) ≈ f (x, y)) • [y � → a] is in M. Then we can make a bad choice and decide p(b) � ≈ t. We now add p(c) ≈ t to M using Decide, instantiate the first formula of W 2 with c ∈ T (M) and apply T -Propagate, UnitPropagate, and Trigger-Unfold so that (∀x. f (x, y) ≈ f (x, y)) • [y � → c] is in M. Since we have a conflict clause in M, we can use T -Backjump but, instead of adding p(b) ≈ t, we make another bad choice and add

(∀x. f (x, y) ≈ f (x, y))•[y � → c]. Indeed, since (∀x. f (x, y) ≈ f (x, y))•[y � → a] ∈ M and G 2 � T a ≈ c, G 2 ∪ M � � T (∀x. f (x, y) ≈ f (x, y)) • [y � → c].
Because of this closure, we can produce an infinite number of terms f (t, c), f ( f (t, c), c) . . . Since we do not have M � T p(c) ≈ t, they are not all equal to t in M. Indeed, we are not bound to add a ≈ c to M until there is nothing else to do even if it is implied by G 2 .

In the rule T -Learn, for a new guarded clause H → C to be learned, every atom of C must occur in AVB(F, H) ∪ LIT(M). Even asking that AVB(F, H) � � T C is not enough to prevent elements that are protected by a trigger in F from occurring in C without their trigger. When they are in C, they can be added to M, through Decide for example, and prevent the solver from terminating. The following example closely resembles the previous one. Assume that closures of C are allowed to occur in M and consider the axiomatization W 2 and the set of user clauses G 2 from the previous paragraph. We can add p(a) ≈ t and p(c) ≈ t to M using UnitPropagate and Decide. We instantiate the first formula of W 2 with a and c ∈ T (M) and apply T -Propagate, UnitPropagate, and Trigger-Unfold so that ([p(y

) ≈ t]∀x. f (x, y) ≈ f (x, y)) • [y � → a] ∧ (p(y) ≈ t) • [y � → a] → (∀x. f (x, y) ≈ f (x, y)) • [y � → a] is in F and (∀x. f (x, y) ≈ f (x, y)) • [y � → c] is in M. If the condition of T -Learn were relaxed, the guarded clause ([p(y) ≈ t]∀x. f (x, y) ≈ f (x, y)) • [y � → a] ∧ (p(y) ≈ t) • [y � → a] → (∀x. f (x, y) ≈ f (x, y)) • [y � → c] could be added to F using T -Learn. Indeed, G 3 ∪ {c ≈ c • ∅, (∀x. f (x, y) ≈ f (x, y)) • [y � → a]} ⊆ AVB(F, {([p(y) ≈ t]∀x. f (x, y) ≈ f (x, y)) • [y � → a], (p(y) ≈ t) • [y � → a]}) and, since G 3 � T a ≈ c, G 3 ∪ {c ≈ c • ∅, (∀x. f (x, y) ≈ f (x, y)) • [y � → a]} � � T (∀x. f (x, y) ≈ f (x, y)) • [y � → c]
. Now, we remove everything from M using Restart. Using T -Propagate and UnitPropagate, we can add p(a) ≈ t, ([p(y

) ≈ t]∀x. f (x, y) ≈ f (x, y)) • [y � → a], (p(y) ≈ t) • [y � → a] and finally (∀x. f (x, y) ≈ f (x, y)) • [y � → c] to M.
Because of this closure, we can produce an infinite number of terms f (t, c), f ( f (t, c), c) . . . Since we do not have p(c) ≈ t, they are not all equal to t in M. Indeed, we are not bound to add a ≈ c to M until there is nothing else to do even if it is implied by G 3 .

In the rule T -Forget, we forbid the deletion of a guarded clause H → C ∈ F if, after the deletion, there is a closure defined in M that no longer appears in AVB(F, H). This is needed so that we have a progress property in spite of the additional constraints on T -Backjump and T -Learn. For example, assume F contains a redundant guarded clause H → C such that H ⊆ M and there is a tautology ϕ •σ ∈ C such that ϕ •σ does not appear in F \{H → C}. The anti-closure ¬(ϕ • σ ) can be added to M using Decide. If H → C is then erased from F with T -Forget, the rule T -Backjump can no longer be applied to revert ϕ • σ .

We also require that AVB(F, H) � � T C. Assume that we can forget H → C as soon as we have F, H � � T C. Consider the axiomatization:

W 4 = {[p(a) ≈ t]∀x. f (x, a) ≈ x, [p(c) ≈ t]∀x. f (x, c) ≈ f (x, c), a ≈ c, a ≈ a, c ≈ c}
Like W 2 , W 4 is terminating. We launch the solver on the set of user clauses

G 4 = {p(a) ≈ t, p(c) ≈ t, p(a) � ≈ t ∨ a ≈ c}. We can easily check that W 4 • ∅ \ {a ≈ c • ∅} ∪ G 4 � � T a ≈ c • ∅,
and therefore we forget it. We can add p(c) ≈ t and the second axiom of W 4 to M using UnitPropagate. With Trigger-Unfold and then T -Propagate and UnitPropagate we can add ∀x. f (x, c) ≈ f (x, c) to M. Because of this closure, we can produce an infinite number of terms f (t, c), f ( f (t, c), c) . . . Since we do not have a ≈ c, they are not all equal to t in M.

In the rule Instantiate, an instance of a formula ∀x.C • σ with a term t cannot be added to

F if AVB(F, M) � � T C • (σ ∪ [x � → t]
). This constraint is needed for termination so that redundant instances are forbidden.

Soundness and Completeness

We show that DPLL � (T ) is compliant with the semantics defined in Section 2.1.2.

Lemma 4.6. For every derivation M

1 � F 1 =⇒ � M 2 � F 2 , every model L of F 1 is a model of F 2 .
Proof. We proceed by case analysis over the rule applied for the step

M 1 � F 1 =⇒ M 2 � F 2 .
• In UnitPropagate, Decide, Restart, T -Propagate, and T -Backjump, F 1 and F 2 are equal.

• For T -Learn, we have F 1 , H � � T C. Since L is complete, by Lemma 4.1, if L � F 1 and L � H, then L � C. • For T -Forget, F 2 ⊆ F 1 . Thus, if we have L � F 1 then L � F 2 .
• For the rule Witness-Unfold, assume that L � �l�C • σ . By definition of �, L � l • σ and L � C • σ .

• For the rule Trigger-Unfold, assume that

L � [l]C • σ ∧ l • σ . By definition of �, L � C • σ .
• For the rule Instantiate, assume that

L � ∀x.C • σ ∧ x ≈ x • [x � → t]. By definition of �, L ∪ known(T (L)) � T known(T (t)
) and so there is

t � ∈ T (L) such that L � T t ≈ t � . Therefore, L � C • (σ ∪ [x � → t]). Lemma 4.7. For every derivation M 1 � F 1 =⇒ � M 2 � F 2 , we have F 2 � � T F 1 .
Proof. Let L be a complete and satisfiable set of literals. We proceed by induction over the number of applications of

T -Forget M � F, H → C =⇒ M � F. If there are none then F 1 ⊆ F 2 .
Otherwise, consider the last application M � F, H → C =⇒ M � F. We have that F ⊆ F 2 and, by induction hypothesis, Proof. We define W to be the result of the Skolemization and the clausification of Ax. Every model of Ax can be extended to a model of W by adding the interpretations of the Skolem functions. As a consequence, since Ax is sound, for every T � -satisfiable set of literals G � that only contains literals of G, there is a model of W ∪ G � . We first need an intermediate lemma. It states that every element of a set of super-literals M constructed in a derivation is either a decision or implied by the input problem and previous decisions:

F ∪ H → C � � T F 1 . Since F � � T C, F � � T C by Lemma 4.3.
Lemma 4.8. If ∅ � G ∪ W • ∅ =⇒ � M 0 e d 1 M 1 . . . e d n M n � F, L is a model of G ∪ W • ∅ and L � e 1 , .
. . , e i then L � M i for every i in 0 . . . n.

Proof. Let L be a model of G ∪W • ∅, such that L � M. We show that, for every rule that adds a new super-literal e to M from M � F (except Decide), L � e. Proof. We define W to be the result of the Skolemization and the clausification of Ax. If W is feasible then so is Ax. As a consequence, since Ax is complete, every set of literals L such that W ∪ L is feasible is T � -satisfiable.

We show that, if the solver returns Sat on a set of clauses G with the theory W then there is a T -satisfiable set of literals L such that L � T G and

W ∪ L is feasible. Since Ax is complete, L is T � -satisfiable. Since L � T G, so is G.
Let F be a set of guarded clauses and M a set of literals and closures such that ∅ � G ∪W • ∅ =⇒ � M � F and:

(i) M � � T AVB(F, M), (ii) M � � � T ⊥,
(F, M) � � T C. Consider L = LIT(M) ∪ {lσ | l • σ ∈ M} ∪ {t ≈ t | t ∈ T (M)}. By (ii), L is T -satisfiable.
We need to show that L � T G and L � W , which is the same as L � AVB(W • ∅ ∪ G, ∅). It is sufficient to prove that L � AVB(F, ∅). Indeed, the only rule that can remove an element of W • ∅ ∪ G is T -Forget and, if L � AVB(F, ∅) and AVB(F, ∅) � � T C, by Lemma 4.2, L � C. Now, we only need to show that L � CLO(M). Indeed, M � � T AVB(F, M) is the same as LIT(M)∪ CLO(M) � � T AVB(F, M) and thus L � CLO(M) implies L � AVB(F, M) by Lemma 4.2. For every closure ϕ • σ ∈ M, we prove that L � ϕσ by induction over the size of the formula ϕ.

• l • σ ∈ M. By definition of L, L � lσ . • ∀x.C • σ ∈ M. Let t be a ground term of L. By definition of L, t is a ground term of M. By (iii), AVB(F, M) � � T C •(σ ∪[x � → t]). Since M � � T AVB(F, M), by Lemma 4.4, M � � T C •(σ ∪ [x � → t]). Therefore, there is ϕ ∈ C such that either ϕ • σ � ∈ M and L � T (σ ∪ [x � → t]) ≈ σ � or ϕ is a literal, M � �
T ϕσ , and M ∪ known(T (CLO(M))) � � T known(T (ϕσ )). In the first case, since ϕ is strictly smaller than ∀x.C, we have L � ϕσ � by induction hypothesis and, hence, L � ϕσ . In the second case, L � ϕσ by definition of L. By definition of � on universally quantified formulas, L � (∀x.C)σ .

• �l�C • σ ∈ M. By (iii), we have AVB(F, M) � � T l • σ and AVB(F, M) � � T C • σ . Since M � � T AVB(F, M), by Lemma 4.4, M � � T l • σ and M � � T C • σ . Hence, there is ϕ ∈ C such that either there is a substitution σ � such that ϕ • σ � ∈ M and M � � T σ ≈ σ � or ϕ is a literal, M � �
T ϕσ and, M ∪ known(T (CLO(M))) � � T known(T (ϕσ )). In both cases, L � ϕσ with the same reasoning as for universal quantifiers. In the same way, L � lσ . Therefore, L � (�l�C)σ .

Progress and Termination

We have shown that DPLL � (T ) only allows derivations that are compliant with the semantics of Section 2.1. In this section, we show that, if some restrictions are applied, there cannot be infinite DPLL � derivations. We also show that, within the same restrictions, every derivation that can not continue is terminal, i.e., the solver can return Sat or Unsat.

For termination, we require instantiation to be fair, that is to say that every possible instance should be generated at some point in the search. To define fairness, we use a notion of instantiation level. An instantiation level n for a term t indicates that t is the result of n rounds of instantiation. More formally, if M is a set of super-literals, the instantiation level level M (t) (resp. level M (e)) of a term t (resp. a super-literal e) is either a non-negative integer or a special element ∞. It is defined as the limit of the sequence level i M computed in the following manner: on a term t level i M (t) � min{level i M (e) | e ∈ M and t ∈ T (e)} on a literal l level i M (l) � 0 on a closure or anti-closure level 0 M (e) � 0 if σ is empty and

∞ otherwise ϕ • σ or ¬(ϕ • σ ) level i+1 M (e) � 1 + max{level i M (xσ ) | x ∈ Dom(σ )}
Operations min, max and + are so that, if S is a non-empty set, min(S ∪ ∞) = min(S), min(∅) = ∞, max(S ∪ ∞) = ∞, max(∅) = -1, and 1 + ∞ = ∞. This sequence always converges since the level of every term or super-literal either stays infinite forever or becomes finite at some i and does not change after that. Using this definition, we define the current instantiation level of a set of super-literals M as level(M) = max{level M (e) | e ∈ M}. We enforce fairness by requiring that new instances of level strictly bigger than the current instantiation level are only possible when:

• a truth assignment, as defined in Section 2.1.4, has been reached, and

• every previously available instance of a smaller instantiation level has already been handled.

These two requirements are obtained by a restriction on derivations: Definition 4.4 (Fairness). We say that a derivation is fair if, for every step _ � F =⇒ Me � F where level M (e) > level(M), e has form x ≈ x • [x � → t] and Instantiate can be applied to some universal formula ∀x.ϕ • σ and the term t in M � F. For every such step, if M � is the minimal prefix of M such that t ⊆ T (M � ), then there is a prefix N of M containing M � and ∀x.ϕ • σ such that:

(a) N � � � T ⊥, (b) for every unit super-clause e ∈ AVB(F, ∅), we have �N� � � T e, (c) for every closure �l�C

• σ ∈ N, �N� � � T l • σ and, if C is a unit clause, �N� � � T C • σ , (d) for every closure [l]ϕ • σ ∈ N such that ϕ is a unit clause, if �N� � � T l • σ then we have �N� � � T ϕ • σ , (e) for every closure ∀x.ϕ • σ ∈ M � such that ϕ is a unit clause and for every term t ∈ T (M � ) such that level M (ϕ • (σ ∪ [x � → t])) ≤ level(M), we have �N� � � T ϕ • (σ ∪ [x � → t]
), and (f) for every guarded clause H → C that can be added to the set F by applying Instantiate,

Witness-Unfold or Trigger-Unfold on a closure of M � , if level M (H) ≤ level(M) then AVB(F, M) � � T C.
Remark 4.4. Note that, in a fair derivation, the current instantiation level of every partial model M is finite.

Remark 4.5. Dealing with instantiation levels is not mandatory. To ensure fairness, it suffices to handle unit clauses, triggers and witnesses before generating new instances and to select instances in the order in which they become possible.

Using this definition of fairness, we state some restrictions on derivation that enforce termination:

Theorem 4.3 (Termination).

There is no infinite derivation Der from a state ∅ � G∪W •∅ where W is terminating such that:

• Der has no infinite sub-derivation made only of T -Learn, T -Forget, and redundant

Witness-Unfold, Trigger-Unfold and Instantiate steps,

• the derivation is fair,

• for every sub-derivation of the form: S i- Proof of (ii): The idea of the proof is the following. During the search, the algorithm will go through the instantiation trees of L ∪W , where L is a set of literals from G. Fairness will prevent it from generating too many instances before generating the one instance that will allow the tree to grow. Note that, since the derivation is fair, every element of M has a finite instantiation level. Indeed, if Instantiate can be applied to some universal formula and some term t in M � F then t ∈ T (M).

Let us first construct the sequence of sets of super-literals Z i that will be used to bound M during the search. We call sub-formula of W , an element of the smallest set containing {ϕ | ϕ ∈ C and C ∈ W } and such that, if ∀x.C, �l�C or [l]C is a sub-formula of W , l and every element of C are sub-formulas of W .

We define the sequence Z i such that

Z 0 = {l, l • ∅, ¬(l • ∅) | l or ¬l occurs in G} ∪ {ϕ • ∅, ¬(ϕ • ∅) | ϕ is a closed sub-formula of W } and Z n+1 = Z n ∪ {ϕ • σ , ¬(ϕ • σ ) | ϕ is a sub- formula of W or the equality x ≈ x and T (σ ) ⊆ T (Z n )}.
Remark 4.7. By construction of the sequence Z i , if an element e ∈ M has an instantiation level n in M then e ∈ Z n .

Since W is terminating, for every subset L of the finite set of literals {l | l • ∅ ∈ Z 0 }, we can choose a finite instantiation tree of W ∪ L. We define what is the biggest truth assignment A M occurring in these trees that is implied by the set M at some point in the search. If A is a set of super-clauses, we define UNIT(A) to be the set of unit super-clauses of A.

For every set of super-literals M, we compute a sequence A M i of sets of theory clauses as follows.

A M 0 is the biggest subset of {l • ∅ | l • ∅ ∈ Z 0 } such that �M� � � T A M 0 . A M 1 is the biggest truth assignment of A M 0 ∪W • ∅ such that �M� � � T UNIT(A M 1 )
. Such a truth assignment may not exist, for instance, if W contains a unit theory clause �l�C and

�M� � � � T l • ∅. Let T M be the finite instantiation tree of {l | l • ∅ ∈ A M 0 } ∪ W . If ∀x.C • σ ,t is the new instance added to A M i in T M , then A M i+1 is the biggest truth assignment of A M i ∪C • (σ ∪ [x � → t]) such that �M� � � T UNIT(A M i+1
), if any. We call d M the maximal i for which A M i exists and we define A M as A M d M . For i ∈ 0..d M , let n M i be the number of closures that are in A M i but not in A M i-1 , if any. We define n max and d max to be integers such that, for every subset L of {l | l • ∅ ∈ Z 0 }, the height of the chosen finite instantiation tree T of L ∪W is less than d max and there is less than n max closures in every truth assignment of T. We have that d M < d max and n M i < n max for every M and every i ∈ 0..d M . We call n M the integer ∑ i∈0..d M (n M i + 1) × (n max + 1) (d max -i) . Note that n M models a lexicographic order on the finite sequence n M 0 . . . n M d M . Remark 4.8. By definition, n M depends only on A M and, if

A Me is different from A M , then n Me > n M .
Let m be (n max + 2) d max +1 . We show that, for every state M � F in the derivation, the current instantiation level in M is at most n

M + 1. Thus, if ∅ � W • ∅ ∪ G =⇒ M � F, elements of M have an instantiation level of at most m + 1 in M. By Remark 4.7, M ⊆ Z m+1 .
Let us now do the proof. We show by induction over the derivation of M � F that:

1. the current instantiation level in M is at most n M + 1, and 2. there is a prefix M � of M such that elements of M � have an instantiation level smaller or equal to n M in M and �M � � � � T UNIT(A M ).

If we remove elements from M, we necessarily return to some previous state of M in the derivation, where the two properties hold by induction hypothesis. In an application of T -Backjump Me d N � F =⇒ Me � � F, e � ∈ AVB(F, M) ∪ LIT(Me d N). Thus, the instantiation level of e � in M is smaller than the current instantiation level in M. As a consequence, since the current instantiation level in M is at most n M + 1 by induction hypothesis, the current level in Me � is also at most n M + 1.

For a step M � F =⇒ Me � F, we show that e has an instantiation level of at most n M + 1 in M. The both properties then follow from remark 4.8. By contradiction, assume that e has an instantiation level of n M + 2 in M. Since the derivation if fair, e has form x ≈ x • [x � → t], some universal formula ∀x.C • ϕ can be instantiated with t, and, if M � is the minimal prefix of M such that t ⊆ T (M � ), then there is a prefix N of M containing M � and ∀x.C • ϕ such that:

(a) N � � � T ⊥, (
• σ ∈ N, �N� � � T l • σ and, if C is a unit clause, �N� � � T C • σ , (d) for every closure [l]ϕ • σ ∈ N such that ϕ is a unit clause, if �N� � � T l • σ then we have �N� � � T ϕ • σ ,
(e) for every closure ∀x.ϕ • σ ∈ M � such that ϕ is a unit clause, and for every term

t ∈ T (M � ) such that level M (ϕ • (σ ∪ [x � → t])) ≤ level(M), we have �N� � � T ϕ • (σ ∪ [x � → t]),
(H) ≤ level(M) then AVB(F, M) � � T C.
Since t ∈ T (M � ), there must be an element of M � that has an instantiation level in M of n M + 1 at least and, by induction hypothesis, of n M + 1 exactly. As a consequence, by property 2, there is a prefix M �� of M � such that �M �� � � � T UNIT(A M ). We need two intermediate lemmas: Lemma 4.9. N contains a truth assignment of A M 0 ∪W • ∅.

Proof. By definition of A M 0 , for every literal l

• ∅ ∈ UNIT(A M 0 ), we have that �N� � � T l • ∅. Since UNIT(AVB(F, ∅)) � � T UNIT(W • ∅), by (b), if l ∈ W then �N� � � T l • ∅. Moreover, for every closure ϕ • ∅ ∈ UNIT(W • ∅) such that ϕ is not a literal, ϕ • ∅ ∈ UNIT(AVB(F, ∅)). By (b), �N� � � T UNIT(AVB(F, ∅)). Therefore, for every closure ϕ • ∅ ∈ UNIT(W • ∅) such that ϕ is not a literal, �N� � � T ϕ • ∅ and ϕ • ∅ ∈ N.
What is more, we have:

• For every �l�C such that N � � T �l�C • ∅, �N� � � T l • ∅ and, if C is a unit clause ϕ then �N� � � T ϕ • ∅ by (c). • For every [l]ϕ ∈ UNIT(W ) such that N � � T [l]C • ∅ and N � � T l • ∅, we have �N� � � T ϕ • ∅ by (d).
As a consequence, d M is at least one and A M is a truth assignment. Proof. We do the proof by structural induction over ϕ.

If ϕ is a universally quantified formula, a literal, or a trigger

[l]C • σ such that {l � σ � | l � • σ � ∈ A M } � � T lσ , then A M ∪ ϕ • σ is a truth assignment of A M ∪ ϕ • σ . If ϕ is a witness �l�C then �N� � � T l •σ by (c). If C is not a unit clause, A M ∪ϕ •σ ∪l •σ ∪C •σ is a truth assignment of A M ∪ ϕ • σ . Otherwise, �N� � � T C • σ by (c). By induction hypothesis, there is a truth assignment A of A M ∪ C • σ such that �N� � � T UNIT(A). As a consequence, A ∪ ϕ • σ ∪ l • σ is a truth assignment of A M ∪ ϕ • σ and �N� � � T UNIT(A) ∪ ϕ • σ ∪ l • σ . If ϕ is a trigger [l]C and {lτ | l • τ ∈ A M } � T lσ then �N� � � T l • σ since �M � � � � T UNIT(A M ) and M � ⊆ N. If C is not a unit clause, A M ∪ ϕ • σ ∪ C • σ is a truth assignment of A M ∪ ϕ • σ . Otherwise, we deduce that �N� � � T C • σ by (d),

and, by induction hypothesis, there is a truth assignment

A of A M ∪ C • σ such that �N� � � T UNIT(A). As a consequence, A ∪ ϕ • σ is a truth assignment of A M ∪ ϕ • σ and �N� � � T UNIT(A) ∪ ϕ • σ .
• σ ∈ N, if UNIT(A M ) � � T H then UNIT(A M ) � � T ϕ • σ . If UNIT(A M
) is final, then the same is true for any H → C ∨ ϕ • σ that can be obtained by applying Instantiate.

Proof. We show that, in each case, there is

σ � such that A M is a truth assignment of ϕ • σ � , UNIT(A M ) � � T σ ≈ σ � and UNIT(A M ) ∪ T (UNIT(A M )) � � T known(T (σ )). If H → C ∨ ϕ • σ can be obtained by Witness-Unfold, H is �l�(C ∨ ϕ) • µ such that σ is µ| vars(ϕ) . Thus, since UNIT(A M ) � � T H, there is �l�(C ∨ ϕ) • µ � ∈ A M , such that UNIT(A M ) � � T µ ≈ µ � and UNIT(A M ) ∪ T (UNIT(A M )) � � T known(T (µ)). Since �N� � � T UNIT(A M ), we have both �N� � � T µ ≈ µ � and �N� ∪ T (N) � � T known(T (µ)). Hence, �N� � � T ϕ • µ � | vars(ϕ)
and, by Lemma 4.10, there is a truth assignment A of A M ∪ ϕ • µ � | vars(ϕ) such that �N� � � T UNIT(A). By construction, A is a truth assignment of A M and, by maximality of

A M , A = A M . If H → C ∨ ϕ • σ can be obtained by Trigger-Unfold, there is [l](C ∨ ϕ) • µ and l • µ| vars(l) in H such that σ = µ| vars(ϕ) . Like for Witness-Unfold, there is [l](C ∨ ϕ) • µ � ∈ A M such that UNIT(A M ) � � T µ ≈ µ � . Since UNIT(A M ) � � T l • µ| vars(l) , UNIT(A M ) � � T l • µ � | vars(l) and there is a truth assignment A of A M ∪ ϕ • µ � | vars(ϕ) such that �N� � � T UNIT(A). Since UNIT(A M ) � � T l • µ � | vars(l) , A is a truth assignment of A M and, by maximality of A M , A = A M . If UNIT(A M ) is final and H → C ∨ ϕ • σ can be obtained by Instantiate, there is ∀x.(C ∨ ϕ) • µ and x ≈ x • [x � → t] ∈ H such that σ = (µ ∪ [x � → t])| vars(ϕ) . Since UNIT(A M ) � � T H, there is ∀x.C ∨ ϕ • µ � ∈ A M and t � ∈ T (UNIT(A M )) such that UNIT(A M ) � � T (µ ∪ [x � → t]) ≈ (µ � ∪ [x � → t � ]) and UNIT(A M ) ∪ T (UNIT(A M )) � � T known(T (µ ∪ [x � → t])). Since A M is final, there is C �� ∨ ϕ • σ �� ∈ A M such that UNIT(A M ) � � T (µ � ∪ [x � → t � ]) ≈ σ �� . Since �N� � � T UNIT(A M ), we have both �N� � � T (µ ∪ [x � → t])| vars(ϕ) ≈ σ �� and �N� ∪ T (N) � � T known(T (µ ∪ [x � → t])). Thus, �N� � � T ϕ • σ �� and, by Lemma 4.10, there is a truth assignment A of A M ∪ ϕ • σ �� such that �N� � � T UNIT(A). By construction, A is a truth assignment of A M and, by maximality of A M , A = A M . Since N � � �
T ⊥ by (a), A M cannot be T -unsatisfiable. If A M is not final, let ∀x.C • σ ,t be the new instance added to A M in the instantiation tree T M . We have that ∀x.C

• σ ∈ A M and t ∈ T (UNIT(A M )). If C is not a unit clause, A M ∪ C • (σ ∪ [x � → t]) is a truth assignment of A M ∪ C • (σ ∪ [x � → t]) such that �N� � � T UNIT(A M ∪ C • (σ ∪ [x � → t]
)) which contradicts the definition of A M . Therefore, C is a unit clause. Since �M �� � � � T UNIT(A M ), there is a substitution σ � and a term t

� ∈ T (M �� ) such that ∀x.C • σ � ∈ M �� , M �� � � T σ ≈ σ � , known(T (M �� )) ∪ M �� � � T known(T (σ )) ∪ known(T (t)) and M �� � � T t ≈ t � . Since ∀x.C • σ � and t � are in M �� ,

this instance has an instantiation level smaller or equal to n

M + 1. By (e), �N� � � T C • (σ � ∪ [x � → t � ]). Since M �� ⊆ N, �N� � � T C • (σ ∪ [x � → t])
. By Lemma 4.10, there is a truth assignment

A of A M ∪ C • (σ ∪ [x � → t]) such that �N� � � T UNIT(A)
which contradicts the definition of A M . Therefore A M is final and no new instance is possible in A M . Consider the universal formula ∀x.C • σ ∈ N that we can instantiate with the term t ∈ T (M � ) by fairness. Let us show that we have AVB(F, M)

� � T C • (σ ∪ [x � → t]
), which contradicts the non-redundancy condition of Instantiate.

We first show that, for every ϕ

• σ ∈ N, UNIT(A M ) � � T ϕ • σ . By contradiction, let ϕ • σ be the first closure of N such that UNIT(A M ) � � � T ϕ • σ . Let M • (ϕ • σ ) � F • be the state after ϕ • σ was added. If ϕ • σ ∈ GRD(F • ) was added to M • using T -Propagate, then UNIT(A M ) � � T ϕ • σ . Indeed, UNIT(A M ) implies every closure ϕ • σ in M •
and also l • ∅ for every user literal in l ∈ M. By construction, if ϕ • σ was added by any other rule, ϕ • σ occurs in AVB(F • , M • ). As a consequence, either ϕ • σ ∈ C � , for some C � ∈ W • ∅, or there is a guarded clause H → C that can be obtained by either Witness-Unfold, Trigger-Unfold, or Instantiate such that

ϕ • σ ∈ C and H ⊆ M • . If ϕ • σ ∈ C � , for some C � ∈ W • ∅, then ϕ • σ ∈ A M , by construction of A M 1 .
Otherwise, there is a guarded clause H → C that can be obtained by either Witness-Unfold, Trigger-Unfold, or Instantiate such that ϕ

• σ ∈ C and UNIT(A M ) � � T H. By Corollary 4.1, UNIT(A M ) � � T ϕ • σ . As a consequence, for every closure ϕ • σ ∈ N, UNIT(A M ) � � T ϕ • σ . Since LIT(M) ⊆ A M by construction, there is a term t � ∈ T (A M ) and a substitution σ � such that ∀x.C • σ � ∈ A M , UNIT(A M ) � � T σ ≈ σ � , known(T (UNIT(A M ))) ∪ UNIT(A M ) � � T known(T (σ )) ∪ known(T (t)), and UNIT(A M ) � � T t ≈ t � . Since A M is final, there is a theory clause C • σ �� ∈ A M such that UNIT(A M ) � � T σ �� ≈ (σ � ∪ [x � → t � ]). We only need to show that AVB(F, M) � � T C • σ �� . Indeed, since �M �� � � � T UNIT(A M ), we can deduce AVB(F, M) � � T C • (σ ∪ [x � → t])
. By construction of A M , we are in one of three cases:

• There is �l�C • σ �� ∈ A M . Since �M �� � � � T UNIT(A M ), there is �l�C • τ ∈ M �� such that M �� � � T σ �� ≈ τ and M �� ∪ known(T (M �� )) � � T known(T (σ �� )). As a consequence, by (f), AVB(F, M) � � T C • σ �� . • There is [l]C • σ �� ∈ A M such that UNIT(A M ) � � T lσ �� . Since �M �� � � � T UNIT(A M ), there is [l]C • τ ∈ M �� such that M �� � � T σ �� ≈ τ, M �� ∪ known(T (M �� )) � � T known(T (σ �� )), and
M �� � � T lσ �� . Therefore, by (f), AVB(F, M) � � T C • σ �� . • There is ∀y.C • (σ �� \ [y � → yσ �� ]) ∈ A M and yσ �� ∈ T (UNIT(A M )). Since we have �M �� � � � T UNIT(A M ), there is ∀y.C • τ ∈ M �� and s ∈ T (M �� ) such that M �� � � T (σ �� \ [y � → yσ �� ]) ≈ τ, M �� � � T yσ �� ≈ s, and M �� ∪ known(T (M �� )) � � T known(T (σ �� )). As a consequence, by (f), AVB(F, M) � � T C • σ �� . Consequently, property 1 holds.
We finally need a progress property. Every derivation that does not allow the solver to terminate can be extended without breaking the restrictions requested for termination. What is more, we only require the rule T -Propagate to be applied when �M� � � T e. We need an intermediate lemma: Proof. Let H → C be a conflict clause in the state M � F. By definition, H ∧ ¬C ⊆ M and H → C ∈ F. We define a sequence e i of literals and a sequence M i of subsequences of M such that M can be written M 1 e d 1 . . . M n e d n M n+1 and M i contain no decision super-literals. We write M i for the prefix . . . M i of M.

Let us show that, for every

D ⊆ M such that F ∪ D � � T ⊥, there is an application M � F =⇒ M j ¬e i � F of T -Backjump.
We do this proof by induction on position of the last and the beforelast element of D in M. In other words, we can use the induction hypothesis on a set of superliterals D � if either there is an element of D that appears strictly after every element of D � in M or if the last element e of D in M is in D � and the before-last element of D in M appears strictly after every element of D � \ e in M.

If every element of

D is in M 1 then there is an application of T -Backjump M � F =⇒ M 1 ¬e 1 � F.
If the element of D that occurs last in M is a decision literal e i , let j ≤ i be the smallest index such that D \ e i ⊆ M j and e i occurs in AVB(F, M j ) (by definition of Decide such a j always exists). If j = 1 or e j-1 ∈ D or e i does not occur in AVB(F, M j-1 )1 then F ∪ (D \ e i ) � � T ¬e i and e i is undefined in M j . As a consequence, there is a T -Backjump step M � F =⇒ M j ¬e i � F.

Otherwise, let e be the element of D that occurs last in M if it is not a decision super-literal and the element of D that occurs before last in M otherwise. Let M � be such that M = M � e . . . . By hypothesis, e is not a decision literal. Thus, the super-literal e must have been added to the partial model by one of the rules UnitPropagate, T -Propagate, or T -Backjump. We show that, in each case, there is a set of super-literals

D � ⊆ M � such that F ∪ D � � �
T e . We then consider the set D �� = (D \ e) ∪ D � on which we can apply the induction hypothesis.

• If e was added to M � using UnitPropagate, then there is a clause H → C ∨ e such that H ∪ ¬C ⊆ M � and F � � T H → C ∨ e by Lemma 4.7. Thus, F ∪ H ∪ ¬C � � T e.

• If e was added to M � using T -Propagate, then M � � � T e by Lemma 4.5. Let S be a minimal subset of M � such that S � � T e. We have F ∪ S � � T e.

• If e was added to M � using T -Backjump, there is a set of super-literals D ⊆ M � and a set of guarded clauses Corollary 4.2. If here is a closure or a literal e such that ¬e ∈ M and �M� � � T e, then a conflict clause can be learned so that either Fail or T -Backjump can be applied.

F � such that F � ∪ D � � T e

Proof. Since �M� � �

T e, there is a set of closures S ⊆ �M� such that S � � T e. We construct a guarded clause H → e that can be added to F using T -Learn. If e is a literal, let H be S itself. Otherwise, since ¬e ∈ M is an anti-closure, e occurs in AVB(F, M). Indeed, a guarded clause H → C of F cannot be forgotten if there is a closure of C defined in M that does not occur in AVB(F \ H → C, H). Let H ⊆ �M� be a superset of S such that e occurs in AVB(F, H). Now, we can add H → e to F using T -Learn. By definition of �M�, closures of H either are already in M or can be propagated using T -Propagate without breaking the fairness property. As a consequence, H → e is a conflict clause and either Fail or, by Lemma 4.11, T -Backjump can be applied on M � F. Remark 4.10. This proof also shows that the definition of fairness does not constrain the choice of instantiating eagerly or lazily, namely after or before deciding on literals of a disjunction. If a decision is possible, then it is allowed and, if an instance is possible, then it will be allowed or redundant after some steps that do not involve any decision.

Proof. If the solver cannot return on M � F then at least one of the following properties is false:

(i) M � � T AVB(F, M), (ii) M � � � T ⊥, and (iii) if H → C can be added by either Instantiate, Witness-Unfold, or Trigger-Unfold then AVB(F, M) � � T C. Assume (i) is false in M � F. If there is a guarded clause H → C ∈ F such that H ∪ ¬C ⊆ M, then H → C is a conflict clause in M � F,
• σ ∈ M, �M� � � T l • σ and, if C is a unit clause, �M� � � T C • σ , (d) for every closure [l]ϕ • σ ∈ M such that ϕ is a unit clause, if �M� � � T l • σ then we have �M� � � T ϕ • σ , (e) for every closure ∀x.ϕ • σ ∈ M such that ϕ is a unit clause and for every term t ∈ T (M) such that level M (ϕ • (σ ∪ [x � → t])) ≤ level(M), we have �M� � � T ϕ • (σ ∪ [x � → t]
), and (f) for every guarded clause H → C that can be added to the set F using either Instantiate,

Witness-Unfold or Trigger-Unfold on M, if level M (H) ≤ level(M), AVB(F, M) � � T C.
Condition (a) can not be false if (i) is true. If (b) is false then there is a unit super-clause e ∈ AVB(F, ∅) such that e � ∈ M. If ¬e ∈ M, ∅ → e is a conflict clause in F. Otherwise, by construction, e is of level 0 in M and e can be added to M using UnitPropagate.

If (c) is false, there is a closure �l�C • σ ∈ M such that �M� � � � T l • σ or C is a unit clause and �M� � � � T C •σ . Therefore, the rule Witness-Unfold can be applied with �l�C

•σ . If it is redundant, AVB(F, M) � � T l • σ and AVB(F, M) � � T C • σ . Therefore, either ¬(l • σ ) or ¬(C • σ ) (if it is a unit clause) is in M
and there is a conflict clause in F after the application of Witness-Unfold or one of l • σ , C • σ can be added to M using UnitPropagate.

If (d) is false, there is a closure

[l]ϕ • σ ∈ M such that �M� � � T l • σ and �M� � � � T ϕ • σ .
Hence Trigger-Unfold can be be applied with [l]ϕ • σ . If it is redundant, either l • σ can be added to M using T -Propagate, there is a conflict clause, or ϕ • σ can be added to M using UnitPropagate.

If (e) is false, there is a closure ∀x.ϕ • σ ∈ M and a term t ∈ T (M) such that ϕ

• (σ ∪ [x � → t]
) has an instantiation level smaller than the current instantiation level in M and

�M� � � � T ϕ • (σ ∪ [x � → t]). First assume that AVB(F, M) � � � T ϕ • (σ ∪ [x � → t]). Then, Instantiate can be applied with ∀x.ϕ • σ . If it is redundant then either x ≈ x • [x � → t] can be added to M using T -Propagate, there is a conflict clause, or ϕ • (σ ∪ [x � → t]) can be added to M using UnitPropagate. If AVB(F, M) � � T ϕ • (σ ∪ [x � → t]) then we have UNIT(AVB(F, M)) � � T ϕ • (σ ∪ [x � → t]). By Lemma 4.4, �M� � � � T UNIT(AVB(F, M)) (otherwise, �M� � � T ϕ • (σ ∪ [x � → t])).
Then, there is a guarded clause H → e ∈ F such that H ⊆ M and M � � � T e. Since H ⊆ M, e has an instantiation level smaller than the current instantiation level in M and can be added to M using UnitPropagate.

Otherwise, the property (f) is false and Trigger-Unfold, Witness-Unfold, or Instantiate can be applied with level

M (H) ≤ level(M) so that either AVB(F, H) � � � T C or AVB(F, H) � � T C and H � M. If AVB(F, H) � � � T C
then the application of Trigger-Unfold, Witness-Unfold, or Instantiate is non-redundant in F. Otherwise, �M� � � T H and, for some l • σ ∈ H \ M, l • σ ∈ GRD(F). If ¬(l • σ ) ∈ M, we conclude using Corollary 4.2. Otherwise, T -Propagate can be applied to l • σ . Indeed, since level M (H) ≤ level(M), it is not forbidden by fairness.

Implementation

In this section, we present our implementation of the framework of Section 4.1.2 in the Alt-Ergo theorem prover. We discuss various specificities of this implementation and finally give some benchmarks using the theory of doubly-linked lists given in Section 2.2.1.

E-Matching on Uninterpreted Sub-Terms

Instantiating every universal quantifier with every known term is really inefficient. All the more since some instances are not usable because there is a trigger directly behind the universal quantifier. First-order SMT solvers commonly use a powerful technique, called E-matching [START_REF] De Moura | Efficient E-matching for SMT solvers[END_REF], to determine the set of instances for which we will be able to remove the trigger just behind the universal quantifier. In our implementation, we would like to use this technique as much as possible. However, we have a constraint that is not usually required in SMT solvers: we need the matching algorithm to be complete. Indeed, this is needed not only for completeness of our solver but also for termination which is mandatory to allow an eager instantiation mechanism.

There are two possibilities to easily turn incomplete E-matching techniques into a complete instantiation mechanism. The first one is to restrict the input language so that axiomatizations can only use some restricted form of triggers on which E-matching is complete. The second one, which we have chosen, is to apply E-matching on parts of triggers on which it is complete and then to check the remaining ones.

More formally, assume that we have an E-matching implementation that is complete on terms that only contain uninterpreted symbols. For every closure ϕ • σ where ϕ is a universally quantified formula ∀x.[l 1 , . . . , l n ]ϕ � where ϕ � is not a trigger, we compute a triple made of a set of literals l ϕ and two sets of terms, p ϕ and k ϕ . It has the following properties:

(i) every free variable (that is not in the domain of σ ) in l ϕ or k ϕ is also in p ϕ , (ii) terms of p ϕ only contain variables and uninterpreted symbols, (iii) if τ is a mapping from free variables of p ϕ to terms containing only variables that are in the domain of σ , then, for i in 1..n:

known(T (σ ) ∪ T (τσ ) ∪ T (p ϕ τσ ) ∪ k ϕ τσ ) ∪ l ϕ τσ � T known(T (l i ))τσ
To instantiate the closure ϕ • σ , we use the matching algorithm on p ϕ σ to get a substitution τ from free variables of p ϕ to known terms. We then wait for every term in k ϕ τσ to appear in M, and every literal of l ϕ τσ ∪ l i σ to be true in M to do the actual instantiation.

To compute the triple, we proceed in the following way. We associate a fresh variable x t to every sub-term t of a literal l i such that t begins with an interpreted function symbol. For every sub-term t of a literal l i such that t begins with a uninterpreted function symbol, and t does not appear as an argument of a uninterpreted function symbol in l i , we create a pattern p t by replacing every sub-term t � of t that begins with an interpreted function symbol by the variable x t � . We now define p ϕ to be the set of all the patterns p t constructed above; k ϕ to be the set of all the sub-terms t of a literal l i beginning with an interpreted function symbol such that x t does not appear in p ϕ ; and l s to be the set of all the equalities x t ≈ t where t is a sub-term of l i beginning with an interpreted function symbol and x t is not in k ϕ .

Example 4.1. Assume that we have a trigger f (g(z, 2 × (y + h(z)))) ≈ f (y) where f , g, and h are uninterpreted and z and y are universally quantified variables. We can compute the three sets:

p ={ f (g(z, x 2×(y+h(z)) )), h(z), y, f (y)} k ={y + h(z)} l ={x 2×(y+h(z)) ≈ 2 × (y + h(z))}
Then, applying E-matching on p, we get substitutions [x 2×(y+h(z)) � → t x , y � → t y , z � → t z ] such that f (g(t z ,t x )), h(t z ), f (t y ) and all their subterms are in T (M) modulo M. To finish the instance, we only need to wait for t y + h(t z ) to be known and t x ≈ 2 × (t y + h(t z )) and f (g(t z ,t x )) ≈ f (t y ) to be true.

Different Notions of Termination

The notion of termination in Section 2.1.4 may turn out to be too constraining for some axiomatization. Let us start with an example. Assume that we want to add to our theory in Section 2.2.1 a predicate symbol that states that a list is a palindrome is_palindrome(co : list) : bool. In an axiomatization of this concept, we could have:

IS_PALINDROME_DEF: ∀co : list.[is_palindrome(co)]is_palindrome(co) ≈ t → (∀cu 1 : cursor.[element(co, cu 1 )]position(co, cu 1 ) > 0 → ∃cu 2 : cursor.�position(co, cu 2 ) = length(co) -position(co, cu 1 ) + 1� equivalent_elements(element(co, cu 1 ), element(co, cu 2 )) ≈ t)
Unfortunately, such an axiom would introduce a loop. If the input set of literals includes the literal is_palindrome(co) ≈ t and the term element(co, cu) is known for some cu, there is a branch deducing the term element(co, sko(co, cu)) which itself allows the deduction of the term element(co, sko(co, sko(co , cu))) and so on. We can see that the term sko(co, sko(co, cu)) is in fact equal to cu, using POSITION_EQ. However, our definition of truth assignment is not restrictive enough to enforce this deduction.

The definition of truth assignment given in Section 2.1.4 can easily be made more or less restrictive. This results in a more or less constraining notion of fairness in the proof of termination of the solver Section 4.1.5. Here are a few examples of alternative choices:

1. Require that at least an element ϕ i • σ i is added to assignments containing a disjunction

ϕ 1 • σ 1 ∨ • • • ∨ ϕ n • σ n (in
the definition of Section 2.1.4, assignments are allowed to contain none). In practice, this amounts to enforcing a lazy instantiation approach, that is to say that new instances can only be generated when enough literals have been assigned a truth value by the model to imply every clause.

2. Require that, if ϕ 1 σ 1 . . . ϕ n-1 σ n-1 are literals that are false in an assignment containing a disjunction 3. Do not require that ϕ is added to assignments containing a witness �l�ϕ or a trigger [l]ϕ with l true. The rules Witness-Unfold and Trigger-Unfold do no longer have to be applied eagerly (before new instances are made).

ϕ 1 • σ 1 ∨ • • • ∨ ϕ n • σ n then ϕ n • σ n is
The first two alternatives would allow the proof of termination of the is_palindrome example to go through. The first one has the drawback of forbidding an eager instantiation of universal quantifiers that can be profitable in practice. We have implemented the second alternative in Alt-Ergo.

Another possibility that we have implemented is allowing the solver to add to truth assignments negations of closures that occur in disjunctions. Termination becomes difficult to achieve with this modification since any universal quantifier can be turned into an existential one and then cause the creation of a new term. To alleviate it, we do not allow negations of closures to be added for the last, or the unique, element of a disjunction. This restriction is motivated by the fact that axioms are often written as implications, the guards being in general simple enough to avoid causing too many new instances.

In general, this gives a more constraining version of termination. Still, if every pseudoclause occurring in an axiomatization can be written l 1 → . . . l n → ϕ, which is the case for the theory of doubly-linked lists, then only anti-closures of literals can be added to truth assignment. Such anti-closures can only shorten branches in which they are introduced and, therefore, cannot compromise termination. As compensation for this generally more constraining version of termination, new rules can be added to DPLL � (T ) to handle anti-closures. They have to work in a compatible way with the semantics of negations of closures defined for the proofs of Section 4.1.4. Existential quantifiers arising from the negation of a universally quantified formula can be handled by associating a priori a Skolem constant to every universal quantifier in the axiomatization.

Using anti-closures, we have restrained termination further by requiring that, for an element

ϕ i • σ i of a theory clause ϕ 1 • σ 1 . . . ϕ n • σ n
or its negation to be added to a truth assignment A, ¬(ϕ k σ k ) must also be added for every k strictly smaller than i. This amounts to requiring that, in DPLL � (T ), decision on a closure of a theory clause C must only occur when C is not already true in the current model and must always decide on the first closure of C that is not assigned to false. Remark that we do not lose completeness since it does not depend on the order of decisions.

Inclusion into the Theory Combination Mechanism

The notion of satisfiability in Section 2.1.2 can also turn out to be too constraining. For example, in the proof of completeness of Section 2.2.3, we need to show that adding equalities on known terms of integer type does not break the partial model. This lemma is needed because we cannot assume that, for a partial model L and two known terms t 1 and t 2 such that L � T t 1 ≈ t 2 , we can find an interpretation I of L such that, I(t 1 ) � = I(t 2 ). Indeed, linear integer arithmetic is not a convex theory, which means that there can be a set of literals L and a set of terms t 1 , s 1 , . . . ,t n , s n ⊆ T (L) such that L � T t 1 ≈ s 1 ∨ • • • ∨t n ≈ s n and, for every i ∈ 1..n, L � T t i ≈ s i . As a consequence, if an axiomatization does not have the above property, it will not be complete in our framework.

To have a less constraining notion of completeness, the definition of satisfiability can be modified. Let F be an axiomatization. Instead of searching for any T -satisfiable set of literals L such that L � F, we can restrict the definition of partial models so that we only search for Tsatisfiable sets of literals L such that, if

L � T t 1 ≈ s 1 ∨• • •∨t n ≈ s n for t 1 , s 1 , . . . ,t n , s n ⊆ T (L) then L � T t i ≈ s i for some i ∈ 1.
.n. Note that we need to modify the notion of termination accordingly. In the definition of truth assignment, we need to also consider the disjunctions coming from nonconvex theories. Our implementation in Alt-Ergo complies with this second definition by using the disjunctions generated by the theory combination mechanism.

Comparison with Alt-Ergo's Built-In Quantifier Handling

We use the Why3 VC generator version 0.80 and the Alt-Ergo theorem prover version 0.95.2. The implementation instantiates every universally quantified formula of the theory before deciding on literals.

Separated Axioms

Grouped Axioms Alt-Ergo � Alt-Ergo Alt-Ergo � Alt-Ergo 3 is a comparison between the results obtained by giving the axiomatization directly in input to Alt-Ergo and using it as a theory through the new mechanism. We use two slightly different versions of the axiomatization. The first one, named Separate Axioms is the one presented in Section 2.2. In the second one, we have grouped the axioms for each function symbol into one axiom. For example, for delete, we now have only one axiom: DELETE_DEF:

∀co 1 co 2 : list, cu : cursor.[delete(co 1 , cu, co 2 )] delete(co 1 , cu, co 2 ) ≈ t → position(co 1 , cu) > 0 ∧ length(co 2 ) + 1 ≈ length(co 1 ) ∧ �next(co 1 , cu)�t∧ (∀cu 2 : cursor.[position(co 1 , cu 2 )] (position(co 1 , cu 2 ) < position(co 1 , cu) → position(co 1 , cu 2 ) ≈ position(co 2 , cu 2 ))∧ (position(co 1 , cu 2 ) > position(co 1 , cu) → position(co 1 , cu 2 ) ≈ position(co 2 , cu 2 ) + 1))∧ (∀cu 2 : cursor.[position(co 2 , cu 2 )] (0 < position(co 2 , cu 2 ) < position(co 1 , cu) → position(co 1 , cu 2 ) ≈ position(co 2 , cu 2 ))∧ (position(co 2 , cu 2 ) ≥ position(co 1 , cu) → position(co 1 , cu 2 ) ≈ position(co 2 , cu 2 ) + 1))∧ (∀cu 2 : cursor.[element(co 1 , cu 2 )] position(co 2 , cu 2 ) > 0 → element(co 1 , cu 2 ) = element(co 2 , cu 2 ))
Indeed, Alt-Ergo, on these examples, works better with separated axioms. It uses a lazy instantiation technique that matches every available trigger once in a while. With the separated axioms, it is the case that it often can instantiate every needed axiom at once while, with the grouped version, it need several matching rounds. On the other hand, the theory mechanism works better with the grouped version as it has less triggers to match.

We see that, on average, our implementation gives better results than the usual instantiation mechanism of Alt-Ergo. This is mainly due to the fact that instances are now generated in an eager way. Note that this is also a drawback in some cases where a lot of work is done even if it was not necessary. This is all the more the case when there are things to prove that do not require theory handling and when the amount of work required by the theory is important.

Conclusion

The formalization of this white box implementation inside DPLL(T ) is significantly more complex than the formalization of the black box implementation in Chapter 3. It is also much more satisfactory in practice as our implementation in the SMT solver Alt-Ergo needs a comparable amount of time to discharge VCs on doubly-linked list as Alt-Ergo's built-in quantifier handling.

Missing in the current implementation is the ability to handle literals as triggers. Like other SMT solvers, Alt-Ergo only accepts triggers that are terms and it would require significant modifications throughout the source to enable literal triggers. Extending Alt-Ergo to literal triggers would be interesting, for example, to handle the extensionality axiom of the theory of arrays. A first approach to handle such triggers would be to match every subterm of such a trigger modulo equality and then to check if the trigger holds in the current partial model. This implementation would not be efficient, in particular for literals that do not have non-variable subterms like x � ≈ y. Coming up with an efficient handling of matching on these triggers would require a tight integration with the theory solvers in order to be able to compute efficiently the set of ground literals true in the current partial model which correspond to a given pattern. This is one direction that we consider for future work.

We are now ready to experiment on more complex case studies. In Chapter 4, we explained how DPLL(T ) can be extended to support first-order logic with triggers. We have also shown that this extension yields a decision procedure when given a sound, complete, and terminating axiomatization as defined in Chapter 4. In this chapter, we apply this approach on the theory of sets provided in the Why3 standard library. This theory is a basis of a larger library of Why3 theories used to formalize the set theory of the B method, which is used in the ANR project BWare1 . It is an industrial research project that aims to provide a framework allowing to automatically discharge verification conditions coming from the verification of safety critical industrial applications using the B method. A generic verification platform is designed that uses several solvers as a back-end. As part of this project, verification conditions coming from the B method are translated into the WhyML language using an axiomatization for polymorphic sets. They can then be discharged by SMT solvers like Alt-Ergo. A benchmark of more than 10,000 verification conditions is provided in this project.

Context: the B Method

The B method [18,[START_REF] Schneider | The B-method: An introduction[END_REF] is a state-based method for designing formally verified software. It starts from a very abstract model of the system that can then be refined until code generation. Properties over the models' data are specified as a mathematical invariant using Zermelo-Fraenkel set theory with the axiom of choice. Mathematical formulas are then generated to ensure both the consistency of the model and the preservation of the invariant through every operations of the model. At each refinement, the invariant becomes more precise as it deals with a more concrete model. This refinement mechanism allows to split the complexity of proof of the final, concrete model, into smaller, simpler proofs.

Verification conditions generated by the B Method are expressed in first-order logic with notations from the Zermelo-Fraenkel set theory with the axiom of choice. This theory includes:

• membership of an element into a set,

• usual set operators like union and intersection between sets,

• the power set of a set s, that is the set containing every subset of s,

• cartesian product of two sets s 1 and s 2 , that is the set of all the pairs of an element of s 1 and an element of s 2 ,

• set comprehension, that is the set of the elements of a set that abide by a given property,

• relations between elements of two sets s 1 and s 2 , represented as sets of pairs (x 1 , x 2 ) where x 1 is an element of s 1 , x 2 is an element of s 2 , and x 1 and x 2 are in relation,

• partial functions, that are relations such that there is at most one pair x 1 , _ in the relation for each x 1 , and

• total functions, that are partial functions such that there is exactly a pair x 1 , _ in the relation for each x 1 .

A Decision Procedure for Why3's Sets

In this section, we present a minimal set theory which we then axiomatize using first-order logic with triggers. This axiomatization is then used to verify verification conditions coming from the BWare project.

Presentation of the Theory

The Why3 platform provides several theories for polymorphic sets. The simplest one, called generic sets, is based on the notion of membership. Sets are described using the function symbol mem(x : α, s : set α) : bool which returns true if and only if the element x is in the set s. The function subset(s 1 : set α, s 2 : set α) : bool returns true if and only if the set s 1 is the subset of a set s 2 , that is, every element of s 1 i a member of s 2 . Two sets are equal if and only if they contain the same elements. The theory describes several functions over sets. The function add(x : α, s : set α) : set α returns the set containing both the element x and the elements of the set s, remove(x : α, s : set α : set α) returns the set containing every element of s except x, and union(s 1 : set α, s 2 : set α) : set α, inter(s 1 : set α, s 2 : set α) : set α, and diff (s 1 : set α, s 2 : set α) : set α return respectively the union, the intersection, and the difference of two sets s 1 and s 2 .

The function is_empty(s : set α) : bool returns true if the set s is equal to the empty set empty. An unspecified element contained in a non-empty set s can be retrieved using choose(s : set α) : α.

Description of the Axiomatization

We only describe some axioms in this section. The whole axiomatization is available in Appendix B.1. Equality between sets is modeled by a the function symbol equal_sets(s 1 : set α, s 2 : set α) : bool. For two sets s 1 and s 2 , equal_sets(s 1 , s 2 ) is true if and only if s 1 and s 2 contain the same elements. The function equal_sets is then related to classical equality by an extensionality axiom. It states that, if equal_sets(s 1 , s 2 ) is true then s 1 and s 2 must be equal. Note that since equal_sets(s 1 , s 2 ) ≈ t can be implied by a set of ground literals L without the term equal_sets(s 1 , s 2 ) to appear in L, we do not put equal_sets(s 1 , s 2 ) as a trigger for EXTENSION-ALITY. We rather use s 1 � ≈ s 2 .

EQ_DEF :

∀s 1 , s 2 : set α.[equal_sets(s 1 , s 2 )]equal_sets(s 1 , s 2 ) ≈ t → (∀x : α.[mem(x, s 1 )]mem(x, s 1 ) ≈ t → mem(x, s 2 ) ≈ t)∧ (∀x : α.[mem(x, s 2 )]mem(x, s 2 ) ≈ t → mem(x, s 1 ) ≈ t) EQ_INV : ∀s 1 , s 2 : set α.[equal_sets(s 1 , s 2 )]equal_sets(s 1 , s 2 ) � ≈ t → (∃x : α.mem(x, s 1 ) ≈ t ∧ mem(x, s 2 ) � ≈ t ∨ mem(x, s 2 ) ≈ t ∧ mem(x, s 1 ) � ≈ t)) EXTENSIONALITY : ∀s 1 , s 2 : set α.[s 1 � ≈ s 2 ]equal_sets(s 1 , s 2 ) ≈ t → s 1 ≈ s 2
For union, inter, and diff , we describe the relationship between members of the set arguments and members of the result. We put triggers so that the axioms can only be applied to deduce new memberships, that is, positive literals starting with the function symbol mem. Indeed, every element that is not known to be in a set can safely be assumed not to be in it. UNION_DEF :

∀s 1 , s 2 : set α, x : α.[mem(x, union(s 1 , s 2 ))] mem(x, union(s 1 , s 2 )) ≈ t → mem(x, s 1 ) ≈ t ∨ mem(x, s 2 ) ≈ t UNION_INV1 : ∀s 1 , s 2 : set α, x : α.[union(s 1 , s 2 ), mem(x, s 1 )]mem(x, s 1 ) ≈ t → mem(x, union(s 1 , s 2 )) ≈ t UNION_INV2 : ∀s 1 , s 2 : set α, x : α.[union(s 1 , s 2 ), mem(x, s 2 )]mem(x, s 2 ) ≈ t → mem(x, union(s 1 , s 2 )) ≈ t
For add and remove, we also describe the result of the modification using the mem function symbol. As for union, we choose triggers so that the axioms can only be used to deduce new memberships. We also need a special case so that mem(y, add(y, s)) ≈ t can be deduced whenever add(y, s) is known. Indeed, otherwise, mem would become incompletely generated as we need to deduce that mem(y, add(y, s)) ≈ t is true in the theory even if the term mem(y, add(y, s)) is not known. For example, to deduce that the set of literals {add(x, empty) ≈ add(y, empty), x � ≈ y} is unsatisfiable we need to know that mem(y, add(y, empty)) ≈ t.

ADD_DEF :

∀x, y : α, s : set α.[mem(x, add(y, s))]mem(x, add(y, s))

≈ t → x ≈ y ∨ mem(x, s) ≈ t ADD_INV1:
∀x, y : α, s : set α.[add(y, s), mem(x, s)]mem(x, s) ≈ t → mem(x, add(y, s)) ≈ t ADD_INV2 :

∀y : α, s : set α.[add(y, s)]mem(y, add(y, s)) ≈ t If a set is empty then it does not contains any element. The function choose returns an element contained in a non-empty set. If a set s is non-empty then it contains choose(s). IS_EMPTY_DEF :

∀s : set α.[is_empty(s)]is_empty(s) ≈ t → (∀x : α.[mem(x, s)]mem(x, s) � ≈ t) CHOOSE_DEF: ∀s : set α.[is_empty(s)]is_empty(s) � ≈ t → mem(choose(s), s) ≈ t ∀s : set α.[choose(s)]is_empty(s) � ≈ t → mem(choose(s), s) ≈ t
Remark that we use the version of CHOOSE_DEF with is_empty(s) as a trigger to express the opposite direction of the implication in the definition of is_empty.

Proofs of Soundness, Completeness, and Termination

In this section, we show that we can use the white-box implementation defined in Chapter 4 on the axiomatization of sets as a decision procedure for the axiomatization of sets where triggers are replaced by implications.

Theorem 5.1. The axiomatization in Section 5.2.2 is terminating, sound and complete with respect to the same axiomatization where triggers are replaced by implications.

Proof of Termination

In the axiomatization, quantified variables are either of type α or of type set α. The axiomatization does not create any term of type set α. The axioms EQ_INV, SUBSET_INV, and CHOOSE_DEF can all create a new term of type α. These three axioms only quantify over variables of type set α. Since this type is uninterpreted by the underlying theory T of the SMT solver, no term of type set α can be created by T . As a consequence, the axiomatization can create a finite number of new terms of type α.

Proof of Soundness

We show that, if a set of literals G has a model in the axiomatization of sets where triggers are replaced by implications then there is a total model of G and the axiomatization. If I is a model of a set of literals G with the axiomatization of sets where triggers are replaced by implications, we define L = {l | I(l) = �}. By construction of L, L is a total model of G. Since L is total, for every axiom ϕ of the axiomatization, L � T ϕ.

Proof of Completeness

Let G be a set of literals and L a world in which G and the axiomatization are true. We construct a model from L in the axiomatization of sets where triggers are replaced by implications. Since L � T G, it is also a model of G.

First of all, we give a value to mem(x, s) for each known term s of type set and each known term x of type α in L. For every s and x such that L � T mem(x, s) ≈ t, we add the literal mem(x, s) � ≈ t to L. Since mem is uninterpreted, the set of literals L is still satisfiable in T .

What is more, the axiomatization is still true in L. Indeed, every clause in the axiomatization protected by a trigger containing mem(x, s) contains a negative occurrence of mem(x, s) ≈ t. As a consequence, they are all automatically satisfied when assuming mem(x, s) � ≈ t.

Then, we need to handle extensionality. For every known terms s 1 and s 2 of type set in L such that, for every known term x of type α, we have L � T mem(x, s 1 ) ≈ t if and only if we have L � T mem(x, s 2 ) ≈ t, we add s 1 ≈ s 2 to L. The set of literals L is still satisfiable. Indeed, since the type set is uninterpreted in the background theory T , if the set L implies a finite clause

s 1 � ≈ s � 1 ∨• • •∨s n � ≈ s � n modulo T then there is i in 1..n such that s i � ≈ s � i .
Thus by EXTENSIONALITY and EQ_INV, there is a term x of type α such that L � T mem(x, s i ) � ≈ mem(x, s � i ). What is more, the axiomatization is still true in L. There can be no trigger l[s 1 ] such L � T l[s 1 ] and L � T l[s 2 ] if, for every known term x of type α, L � T mem(x, s 1 ) ≈ t if and only if L � T mem(x, s 2 ) ≈ t. Indeed, such a case can only happen if either there is a trigger containing twice the same set variable, in which case a new equality may allow this trigger to match, or if there are two nested triggers with the same set variable. The first case never occurs in the axiomatization. As stated previously, using mem(add(y, s), y) as a trigger for ADD_INV2 would have cause such a case to occur and the axiomatization to be incomplete as it could not have found the unsatisfiability in {x � ≈ y, add(x, empty) ≈ add(y, empty)}. The second case though, occurs in several axioms, but, each time, all the nested triggers but one are applications of mem. Since every term mem(x, s) is known in L for s and x known in L, these instances are redundant.

Let us now handle choose and is_empty. For every known term s of type set in L, if s is not equal to empty then add is_empty(s) � ≈ t to L. If there is no term x in L such that L � T mem(x, s) ≈ t then we already have L � T empty ≈ s. In every other case, if the term choose(s) is not known in s, then pick a term x such that L � T mem(x, s) ≈ t and add choose(s) ≈ x to L. The set L is still satisfiable. Since, when assuming a new term is_empty(s), we also ensure mem(choose(s), s) ≈ t, the axiomatization is still true in L.

Finally, we simply add appropriate values to membership of missing terms starting with subset, add, remove, inter, union, and diff .

Benchmarks

In this section, we compare the efficiency of usual E-matching techniques of several SMT solvers on our axiomatization with and without user provided triggers. We also compare Alt-Ergo builtin, lazy, instantiation technique with our, eager, implementation.

From the axiomatization in Section 5.2.2, we remove the extensionality axiom and we leave it as a first-order formula without triggers. Indeed, non-tautological triggers are neither handled by off-the-shelf SMT solvers nor supported by our implementation in Alt-Ergo.

The set theory used by the B method supports constructs that are not part of the Why3 set theory. They can be axiomatized with appropriate first order axioms. That is what was done for power(s), that returns the power set of a set s, and for interval(i, j) that represents the set of integers between i and j.

MEM_POWER:

∀s 1 , s 2 : set α.[mem(s 1 , power(s 2 ))] mem(s 1 , power(s 2 )) ≈ t ↔ subset(s 1 , s 2 ) ≈ t ∀s 1 , s 2 : set α.[power(s 2 ), subset(s 1 , s 2 )] mem(s 1 , power(s 2 )) ≈ t ↔ subset(s 1 , s 2 ) ≈ t MEM_INTERVAL: ∀x, i, j : int.[mem(x, interval(i, j))] mem(x, interval(i, j)) ≈ t ↔ i ≤ x ≤ j
These axioms are terminating and therefore can be added to the theory of sets without compromising termination of the solver. Indeed, none of these axioms can create any new term. Remark that they are not complete though. The sets of literals {is_empty(interval(2, 4)) ≈ t} and {is_empty(power(s)) ≈ t} are unsatisfiable in the theory of sets extended with power and interval but this cannot be deduced by our axiomatization. For these axioms, we have not tried to achieve completeness. Indeed, it would have required generating too many instances, hindering efficiency (one per integer between i and j for interval(i, j) and one per term of type set α for power.

Among the additional first-order axioms that are used by Why3 to model the set theory used by the B method, are a couple of axioms that are specializations of the extensionality axiom: ADD_REMOVE: ∀x : α, s : set α.[add(x, remove(x, s))]mem(x, s) ≈ t → add(x, remove(x, s)) ≈ s UNION_SINGLETON_ADD:

∀x : α, s : set α.[union(s, add(x, empty))]union(s, add(x, empty)) ≈ add(x, s) Those axioms are theoretically redundant but not necessarily useless as properly choosing when to apply the extensionality axiom is difficult. We have done two runs of the BWare bechmarks containing a total of 10572 verification conditions with and without these axioms.

No lemma

With We see that the effect of the additional lemmas depends on the solver. While they seem to hinder Alt-Ergo a lot, they allow CVC4 to discharge a handful more of verification conditions. We can also see that, when the additional lemmas are present, manually written triggers are beneficial for both Alt-Ergo and Z3. Indeed, an SMT solver can make an unfortunate choice for the triggers of additional lemmas for extensionality like ADD_REMOVE and use the term mem(x, s). Such a choice leads to useless instances thus hindering the solver.

The manually written triggers for the set theory do not seem to have a major impact on the efficiency of solvers. A notable exception to this remark is Z3. We can see on Figure 5.2 that the two runs of Z3 on our axiomatization without the additional axioms are close enough until a limit of 10s is reached. Then the curve without the triggers has a bend, as Z3 discharges more verification conditions without the triggers than with them. This bend may be caused for example by a change in the instantiation heuristic triggered by either a big number of instances or a time limit.

To explain this feeble impact of user provided triggers without the lemmas for extensionality, we need to consider the heuristics used inside SMT solvers for choosing instantiation patterns for a quantified formula ∀x 1 . . . . ∀x n .ϕ. The most usual one consists in picking a subterm of ϕ whose free variables are exactly x 1 . . . x n if any. We remark that, on our axiomatization, this heuristic always hands out triggers that are restrictive enough to enforce termination even though they do not ensure completeness.

The fact that we lose the completeness of our triggers will not necessarily lead to poorer performances, especially for Z3 and CVC4 which have other heuristics for instantiating triggers than E-matching. Indeed, while some instances are useful in general for completeness they may very well be useless on our benchmarks and therefore slow down the solver.

Note that a possible reason for explaining why CVC4 and Z3 are less efficient than Alt-Ergo on these verification conditions is that we use the SMT-LIB format for them which does not support polymorphism. An instance of each lemma is therefore generated for each set type in the input problem, of which there may be many.

Comparison

Between our Implementation and the Built-in Quantifiers Handling of Alt-Ergo

The Figure 5.4 compares the time needed to discharge verification conditions with our theory mechanism's implementation in Alt-Ergo and with Alt-Ergo's built-in quantifiers handling on the BWare benchmarks without additional lemmas and with user provided triggers. We see that eager instantiation ends up being rather efficient on these tests. Remark that if MEM_POWER and MEM_INTERVAL are treated by the built-in quantifier handling of Alt-Ergo, the performances of the theory mechanism are poorer as the solver will do eagerly a lot of (unnecessary) set reasoning before instantiating these axioms. We see that choosing a good subset of axioms for eager instantiation is crucial for efficiency. It is even more appearent when the theory handling is time consuming, in particular when its axiomatization leads to numerous instances. Indeed, instances of axioms outside the theory will be sparser, delaying reasoning that requires them.

The inclusion/exclusion of lemmas for extensionality in the theory does not impact our implementation much. We do not even get the important slow-down when we do not put triggers for them. Indeed, those are not eagerly instantiated while other useful axioms are and, therefore, their ill effect is less noticeable.

Note that there are also several verification conditions that are discharged by Alt-Ergo's built-in quantifiers handling but not by our implementation. That can be because there are still a great deal of axioms describing additional constructs such as relations, functions, restrictions, finite sets... that we do not include in the theory.

Conclusion

On this case study, eager instantiation of universal quantifiers gives good results. Indeed, our implementation in Alt-Ergo discharges more verification conditions than Alt-Ergo's built-in handling of quantifiers. We have also seen that, although manually provided triggers are not always profitable to off-the-shelf SMT solvers, well-chosen triggers can prevent the slow-down due to unnecessary quantifier instantiations. In Chapter 4, we explained how DPLL(T ) can be extended to support first-order logic with triggers. We have also shown that this extension yields a decision procedure when given a sound, complete, and terminating axiomatization as defined in Chapter 4.

In this chapter, we explain how the mechanism described in Chapter 4 can be used during the verification of Ada programs dealing with vectors using the SPARK 2014 tool.

Context: the SPARK 2014 Tool

The translation from SPARK 2014 into WhyML does not preserve the structure of the SPARK 2014 code but rather the mathematical formulas that can be extracted from them. Entities in SPARK 2014 are translated into modules in WhyML. For a type in Ada, this WhyML module contains a type and elements needed to axiomatize it. For example, discrete types are modeled using conversion functions into Why3 mathematical integers as well as some bounds.

Here is the WhyML translation of the Ada type for 8 bits integers: The WhyML program def has no parameter. Indeed, all SPARK parameters and local variables of the subprograms are declared as top-level global variables in WhyML. This is done to accommodate cross references between SPARK declarations. def also has no precondition since it needs to produce the mathematical formulas to check that the precondition of the subprogram cannot raise errors at runtime and this cannot be done in the context of the precondition. The precondition is assumed once these mathematical formulas are produced.

Ada's Formal Vectors Package

SPARK 2014 excludes data structures based on pointers. To work around this restriction, the content of a datastructure can be hidden using private types and ghost functions -functions that are allowed only in annotations -can be used to express the model of the structure. Another possibility is to use the library of formal containers provided with SPARK 2014. We have specifically designed and annotated these generic containers (vectors, lists, maps, and sets) to facilitate the proof of programs using them [START_REF] Dross | Correct code containing containers[END_REF].

Description of the Ada Package

The Ada formal vectors package is a generic package. It can be instantiated with a type for indexes, which must be a discrete type, a type for elements, and an equality function over elements that will be used to search for elements in the vector: Two private types are defined, one for vectors and one for cursors. Formal vectors are resizable arrays which are bounded to avoid dynamic memory management, that is, their length cannot be greater than a given bound, named capacity. They are implemented as arrays of elements of size the capacity of the vector, along with an index to give the position of the last valid element in the array. To iterate over vectors, the easiest way is to use indexes. Still, for conformance with other containers' packages, cursors are also provided. Even if they are implemented using indexes, the Ada reference manual only requires two cursors to be equal if they designate the same element in the same container. As a consequence, in the formalization, we cannot assume in general that two cursors at the same index in different vectors are equal. Most of the subprograms defined over vectors are procedures that modify their vector argument. Among them, there are twelve subprograms for insertion, four for deletion, two to swap elements and one to reverse the elements of the vector argument. According to the reference manual, these subprograms preserve cursors except when they are ambiguous, that is when elements have been slided over it by insertion or deletion or when they have been reordered.

Among the four procedures for deletion, the most generic one takes a vector Container, an index Index, and a natural Count: The formal vectors API also contains a few functions returning vectors. There are four concatenation functions and a copy function for which the capacity of the copy can be specified:

Presentation of the Theory

Vectors are resizable arrays of elements on which an equivalence is defined using the function equal_elements(e 1 : element_type, e 2 : element_type) : bool. They are indexed by a range of integers bounded by two integer constants index_type__ f irst and index_type__last. A third constant, index_type__zero, is used to refer to index_type__ f irst -1. The size of a vector co can be retrieved using a function length(co : vector) : int. Vectors have a capacity, modeled by a function capacity(co : vector) : int, which is the maximal size they can reach. The capacity of a vector is such that the vector cannot exceed the index range. For every vector co, the first index of co is index_type__ f irst and its last index is length(co) + index_type__zero.

In a vector co, there is a cursor associated to each index i between the first and the last index of co. It can be retrieved using a function to_cursor(co : vector, i : int) : cursor. A cursor constant, named no_element, is associated to every integer between index_type__zero and index_type__last that is not between the first and the last index of co. A cursor cu is valid in a vector co if and only if there is an index i between the first and the last index of co such that cu ≈ to_cursor(co, i). This property can be expressed using the function has_element(co : vector, cu : cursor) : bool. A cursor can be valid in several vectors. However, it must always refer to the same index in all vectors it is valid in. The index referred by a cursor cu can be accessed using to_index(cu : cursor) : int. The cursor no_element cannot be valid in any vector.

Iteration over cursors is supported thanks to dedicated functions. The function f irst(co : vector) : cursor (resp. last(co : vector) : cursor) yields the cursor associated to the first (resp. the last) index of the vector co. In the same way, if cu is a valid cursor in the vector co, previous(co : vector, cu : cursor) : cursor (resp. next(co : vector, cu : cursor) : cursor) yields the cursor associated to to_index(cu) -1 (resp. to_index(cu) + 1) in co. On no_element, previous and next return no_element. In all other cases, previous and next are not specified.

The element stored in a vector co at an index i between the first and the last index of co can be retrieved using element(co : vector, i : int). For every vector co, every element e, and every integer i, f ind_index(co : vector, e : element_type, i : int) : int (resp. reverse_ f ind_index(co : vector, e : element_type, i : int) : int) returns the first index j greater than i (resp. the last index j less than i) such that equal_elements(element(co, j), e) returns true, if any. If there is no such index between i and the last index of co (resp. between the first index of co and i), the function f ind_index (resp. reverse_ f ind_index) returns index_type__zero.

The theory also includes several predicate symbols used to describe how results of procedures that modify vectors in the API are related to their inputs:

• If replace_element(co : vector, i : int, e : element_type, r : vector) : bool is true then the index i is between the first and the last index of the vector co and the vector r is co where the element at index i was replaced by the element e. The cursors are preserved.

• If insert(co 1 : vector, i : int, co 2 : vector, r : vector) : bool is true then the index i is between the first and the last index of the vector co 1 plus one and the vector r is co 1 where the vector co 2 has been inserted just before i. The cursors associated to indexes strictly smaller than i in co 1 are preserved.

• If swap(co : vector, i 1 : int, i 2 : int, r : vector) : bool is true then the indexes i 1 and i 2 are between the first and the last index of the vector co and r is co where the elements at index i 1 and i 2 have been swapped. The cursors are preserved.

• If delete(co : vector, i : int, l : int, r : vector) : bool is true then the index i is between the first and the last index of the vector co, the non-negative integer l is smaller than the length of co, and the vector r is co where the elements at the l consecutive indexes starting at i have been removed. The cursors associated to indexes strictly smaller than i in co are preserved.

• If delete_end(co : vector, i : int, r : vector) : bool is true then the index i is between the first and the last index of the vector co and the vector r is co where the last elements, starting at index i, have been removed. The cursors associated to indexes strictly smaller than i in co are preserved.

• If reverse_elements(co : vector, r : vector) : bool is true then the vector r contains the elements of the vector co in reverse order and r and co have the same capacity. Cursors are not preserved.

The function copy(co : vector, new_capacity : int) : vector returns a vector that has the same length and contains the same elements and cursors as the vector co. If the natural new_capacity is index_type__zero then the vectors copy(co, new_capacity) and co have the same capacity. If new_capacity is between length(co) and index_type__last -index_type__zero then the capacity of the vector returned by copy is new_capacity. Otherwise, it is not specified.

Like in Ada, there are four concatenation functions: one to concatenate two vectors, named concat__1(co 1 : vector, co 2 : vector) : vector, two to concatenate a vector and an element, named concat__2(co : vector, e : element_type) : vector and concat__3(e : element_type, co : vector) : vector, and one to concatenate two elements, concat__4(e 1 : element_type, e 2 : element_type) : vector. The capacity of the result of these functions is not specified. Neither are the values of its cursors.

Description of the Axiomatization

We only describe some axioms in this section. The whole axiomatization is available in Appendix C.1. We first define a logic type for vectors, along with logic functions for element, length, and capacity. We use two axioms to give bounds for the value returned by these functions: CAPACITY_RANGE:

∀co : vector.[capacity(co)] 0 ≤ capacity(co) ≤ index_type__last -index_type__zero LENGTH_RANGE: ∀co : vector.[length(co)] 0 ≤ length(co) ≤ capacity(co)
The functions used for translation between cursors and indexes are defined as expected. We add a redundant axiom, HAS_ELEMENT__TO_CURSOR, which is not required for termination nor completeness of the axiomatization. It states that has_element(co,to_cursor(co, i)) is true for i such that index_type__zero < i ≤ length(co) + index_type__zero, which can be deduced from the axioms HAS_ELEMENT__DEF and TO_INDEX_TO_CURSOR as soon as the term has_element(co,to_cursor(co, i)) is known. It is not required for completeness since the function symbol has_element does not appear in other triggers of the theory. Still, as verification conditions arising from Ada programs usually contain quantifiers, we may need to deduce that we have has_element(co,to_cursor(co, i)) ≈ t when only to_cursor(co, i) is known. TO_INDEX_RANGE:

∀cu : cursor.[to_index(cu)] index_type__zero ≤ to_index(cu) ≤ index_type__last TO_INDEX_NO_ELEMENT: to_index(no_element) ≈ 0 TO_INDEX_TO_CURSOR: ∀i : int, co : vector.[to_cursor(co, i)] (length(co) + index_type__zero ≥ i > index_type__zero → to_index(to_cursor(co, i)) ≈ i) ∧ (length(co) + index_type__zero < i ∨ i ≈ index_type__zero → to_cursor(co, i) ≈ no_element) HAS_ELEMENT__DEF: ∀cu : cursor, co : vector.[has_element(co, cu)] has_element(co, cu) ≈ t ↔ (to_index(cu) > 0 ∧ to_cursor(co,to_index(cu)) ≈ cu) HAS_ELEMENT__TO_CURSOR: ∀i : int, co : vector.[to_cursor(co, i)] length(co) + index_type__zero ≥ i > index_type__zero → has_element(co,to_cursor(co, i)) ≈ t
To describe previous, next, first, and last, we use to_cursor. Note that these functions are total but are only described on their domain of definition so that nothing can be deduced about illegal applications.

(to_index(cu) = index_type__ f irst ∨ cu ≈ no_element) → previous(co, cu) ≈ no_element
The predicates describing a modification of vectors are only relevant if they are known to be true. Here are axioms describing how the value of a vector after a deletion is related to its previous value. They express the links between vectors using functions length, element, and to_cursor. We take care to only specify the value of partial functions such that element and to_cursor on domains where they are defined. To allow the proof of completeness to go through, we allow equalities between elements and cursors to be usable from both sides. It sometimes requires some axioms to be split in two, like DELETE_ELEMENT_1 and DELETE_CURSORS. Remark that, like in DELETE_ELEMENT_2, we avoid having interpreted symbols in our triggers, element(co 2 , jl) for example, since this makes both reasoning about the axiomatization more complex and the implementation less efficient. DELETE_RANGE:

∀co 1 , co 2 : vector, i, l : int.[delete(co 1 , i, l, co 2 )] delete(co 1 , i, l, co 2 ) ≈ t → index_type__zero < i ≤ length(co 1 ) + index_type__zero DELETE_LENGTH: ∀co 1 , co 2 : vector, i, l : int.[delete(co 1 , i, l, co 2 )] delete(co 1 , i, l, co 2 ) ≈ t → length(co 1 ) + index_type__zero ≈ length(co 2 ) + index_type__zero + l DELETE_CAPACITY: ∀co 1 , co 2 : vector, i, l : int.[delete(co 1 , i, l, co 2 )] delete(co 1 , i, l, co 2 ) ≈ t → capacity(co 1 ) ≈ capacity(co 2 ) DELETE_ELEMENT_1: ∀co 1 , co 2 : vector, i, l, j : int.[delete(co 1 , i, l, co 2 ), element(co 1 , j)] delete(co 1 , i, l, co 2 ) ≈ t → index_type__zero < j < i → element(co 1 , j) ≈ element(co 2 , j) ∀co 1 , co 2 : vector, i, l, j : int.[delete(co 1 , i, l, co 2 ), element(co 2 , j)] delete(co 1 , i, l, co 2 ) ≈ t → index_type__zero < j < i → element(co 1 , j) ≈ element(co 2 , j) DELETE_ELEMENT_2 : ∀co 1 , co 2 : vector, i, l, j : int.[delete(co 1 , i, l, co 2 ), element(co 1 , j)] delete(co 1 , i, l, co 2 ) ≈ t → i + l ≤ j ≤ length(co 1 ) + index_type__zero → �i + l� element(co 1 , j) ≈ element(co 2 , j -l) ∀co 1 , co 2 : vector, i, l, j : int.[delete(co 1 , i, l, co 2 ), element(co 2 , j)] delete(co 1 , i, l, co 2 ) ≈ t → i ≤ j ≤ length(co 2 ) + index_type__zero → element(co 1 , j + l) ≈ element(co 2 , j) DELETE_CURSORS: ∀co 1 , co 2 : vector, i, l, j : int.[delete(co 1 , i, l, co 2 ),to_cursor(co 1 , j)] delete(co 1 , i, l, co 2 ) ≈ t → index_type__zero < j < i → to_cursor(co 1 , j) ≈ to_cursor(co 2 , j) ∀co 1 , co 2 : vector, i, l, j : int.[delete(co 1 , i, l, co 2 ),to_cursor(co 2 , j)] delete(co 1 , i, l, co 2 ) ≈ t → index_type__zero < j < i → to_cursor(co 1 , j) ≈ to_cursor(co 2 , j)
Remark 6.1. In DELETE_ELEMENT_2, we use a witness to make sure that, when the literal element(co 1 , j) ≈ element(co 2 , jl) is assumed by the solver, the terms of the guard i + l ≤ j ≤ length(co 1 ) + index_type__zero are known in the current partial assignment. This is required so that, whenever element(co 1 , j) ≈ element(co 2 , jl) is assumed, we are sure that the guard i + l ≤ j ≤ length(co 1 ) + index_type__zero is true. Indeed, in the variant of termination described in the last paragraph of Section 4.2.2, for element(co 1 , j) ≈ element(co 2 , jl) to appear in a truth assignment A, ¬(i + l ≤ j ≤ length(co 1 ) + index_type__zero) must be false in A. Using the semantics of worlds, we can only deduce i + l ≤ j ≤ length(co 1 ) + index_type__zero if the terms of i + l ≤ j ≤ length(co 1 ) + index_type__zero are known in A.

For copy, and concatenation functions, we introduce a predicate symbol to describe the result of the call. For concatenation, concat(co 1 , co 2 , r) states that r is the result of the concatenation of co 1 and co 2 . The predicate symbol equal_vectors describes the result of copy:

EQUAL_VECTORS__DEF: ∀co 1 , co 2 : vector.[equal_vectors(co 1 , co 2 )] equal_vectors(co 1 , co 2 ) ≈ t → (length(co 1 ) ≈ length(co 2 )∧ (∀i : int.[element(co 1 , i)] index_type__zero < i ≤ length(co 1 ) + index_type__zero → element(co 1 , i) ≈ element(co 2 , i))∧ (∀i : int.[element(co 2 , i)] index_type__zero < i ≤ length(co 1 ) + index_type__zero → element(co 1 , i) ≈ element(co 2 , i))∧ (∀i : int.[to_cursor(co 1 , i)]to_cursor(co 1 , i) ≈ to_cursor(co 2 , i))∧ (∀i : int.[to_cursor(co 2 , i)]to_cursor(co 1 , i) ≈ to_cursor(co 2 , i))) COPY__DEF:
∀co : vector, cap : int.[copy(co, cap)] equal_vectors(co, copy(co, cap)) ≈ t∧

(cap ≈ 0 → capacity(co) ≈ capacity(copy(co, cap)))∧ (length(co) ≤ cap ≤ index_type__last -index_type__zero → capacity(copy(co, cap)) ≈ cap)

Proofs of Soundness, Completeness, and Termination

First, we can note that the axiomatization is not complete in general. Indeed, consider the following set of ground literals:

G =    length(u) ≈ 1, element(u, index_type__ f irst) ≈ e, insert(co 1 , 1, u, co 2 ) ≈ t, delete(co 2 , length(co 2 ), co 1 ) ≈ t, index_type__zero < i ≤ length(co 1 ), element(co 1 , i) � ≈ e   
The set G is unsatisfiable in the axiomatization of vectors without triggers and witnesses. Indeed, every element at index index_type__zero < i ≤ length(co 1 ) in co 1 is equal to the element at index i in co 2 since delete(co 2 , length(co 2 ), co 1 ) ≈ t. But it is also equal to the element at index i + 1 in co 2 since insert(co 1 , 1, u, co 2 ) ≈ t. As a consequence every element of co 1 and co 2 are equal to e. Such a reasoning needs induction and, therefore, is not in the reach of an SMT solver.

Fortunately, this kind of example cannot arise from the verification of an Ada program. Indeed, verification conditions coming from such a program cannot contain equalities between vectors and modification predicates such as insert and delete only appear as the postcondition of an Ada function on its result, which is a fresh vector variable.

Note that we would have liked to include an equivalence relation equivalent_vectors on vectors in our theory to model Ada equality on vectors. Since this would have lead to incompleteness and non termination, we are forced to leave axioms dealing with it to other quantifier handling mechanisms: EQUIVALENT_VECTORS__DEF:

∀co 1 , co 2 : vector.[equivalent_vectors(co 1 , co 2 )] equivalent_vectors(co 1 , co 2 ) ≈ t ↔ length(co 1 ) ≈ length(co 2 ) ∧ (∀i : int.[element(co 1 , i)] index_type__zero < i ≤ length(co 1 ) + index_type__zero → equal_elements(element(co 1 , i), element(co 2 , i)) ≈ t) ∧ (∀i : int.[element(co 2 , i)] index_type__zero < i ≤ length(co 1 ) + index_type__zero → equal_elements(element(co 1 , i), element(co 2 , i)) ≈ t)
Theorem 6.1. The axiomatization in Section 6.3.2 is terminating, sound and complete with respect to the same axiomatization without triggers and witnesses on verification conditions coming from Ada programs.

Proof of Termination

We show that, on verification conditions coming from Ada programs using vectors, our axiomatization is terminating.

Since we have quantification over integers, we cannot, like for lists, show that there can only be a finite number of new terms of each type. Instead, we use the fact that triggers only contain uninterpreted function symbols. We only need to show that there can only be a finite number of terms starting with every uninterpreted function symbol. Indeed, the underlying theory T cannot create a new term starting with such a function symbol and, therefore, if there can only be a finite number of terms starting with every uninterpreted function symbol, there can only be a finite number of new instances.

We use modularity as much as possible. We first consider the axioms that use to_index or capacity as a trigger. There is only one for each, and they do not create any new term except constants. The function symbol length is used as a trigger for only one axiom, that is LENGTH_RANGE. This axiom can create new terms, but only new terms starting with capacity. We already know that such terms cannot lead to an infinite number of new terms.

Most of the remaining function symbols (find, concat, delete, insert...) cannot be created by the axiomatization. Therefore, there can only be a finite number of terms starting with them. In the same way, there can be one term starting with equal_vectors created for each copy term and one concat term for each concat, concat__2, or concat__3 term.

The function symbol has_element can be used to create terms starting with to_index, which cannot lead to an infinite number of new terms, as well as a term starting with to_cursor through HAS_ELEMENT__DEF. The only way to create a term starting with has_element is with the axiom HAS_ELEMENT__TO_CURSOR. It has the term to_cursor(co,i) as a trigger. Proving that these two axioms cannot get into a loop requires a finer grain reasoning. For each term has_element(co,cu), HAS_ELEMENT__DEF can create a term to_cursor(co,to_index(cu)) by assuming to_cursor(co,to_index(cu))≈cu. The axiom HAS_ELEMENT__TO_CURSOR can then create a term has_element(co,to_cursor(co,to_index(cu)). This will not lead to any new instance from HAS_ELEMENT__DEF since has_element(co,cu) is known and to_cursor (co, to_index (cu)) ≈ cu.

Let us now consider the two remaining function symbols, element and to_cursor. They appear in triggers all over the axiomatization but a quick survey of the axioms shows that they either appear as triggers in the first level of quantifiers with a function symbol that can only be created a finite number of times in the axiomatization or as unique trigger directly below a quantifier with a trigger containing only a function symbol that can only be created a finite number of times in the axiomatization. In both cases, we have that, from a given set of ground literals, there can only be a finite number of distinct quantifier instantiations with terms starting with either element or to_cursor as a trigger.

Quantifiers that have the term to_cursor(co,i) as a trigger can only create terms of the form to_cursor(co',i) where co � is a known term of type vector. Since there can only be a finite number of those, the axiomatization cannot create an infinite number of terms starting with to_cursor.

Axioms for find cannot create an infinite number of terms starting with element. For the other axioms, we use the fact that proof obligations are coming from Ada programs. As a consequence, equivalence classes modulo L of known vector terms can be linked in a directed acyclic graph where there is an edge from r to co if r is the result of a vector modification involving co.

We show that, from a set of ground literals L, the axiomatization can only create a finite number a new terms starting with element. Let element(co, i) be a term of L. For every known vector term co � , we define iteratively a set eq L (i,co) (co � ) of pairs of an integer index and a set of literals guards such that L ∪ guards � T element(co, i) ≈ element(co � , index). More precisely, we start with eq L (i,co) (co) = {(i, ∅)} and eq L (i,co) (co � ) = ∅ for every term co � different from co. Then, for every node co � , we define eq L (i,co) (co � ) as the union of its previous value and the values coming from its adjacent nodes. For example, if a node is labeled by co 1 and there is an edge from co 2 to co 1 labeled by delete(co 1 , j, l, co 2 ) then eq L (i,co) (co 2 ) is augmented by:

{(index, guards ∪ {index_type__zero < index < j} | (index, guards) ∈ eq L (i,co) (co 1 ) and L ∪ guards ∪ {index_type__zero < index < j} � T ⊥}∪ {(index -l, guards ∪ { j + l ≤ index ≤ length(co 1 ) + index_type__zero} | (index, guards) ∈ eq(co 1 ) and L ∪ guards ∪ { j + l ≤ index ≤ length(co 1 ) + index_type__zero} � T ⊥}
This construction will terminate. We can do this proof by induction over the number of nodes in the graph. If there is only one node then there is no propagation. Otherwise, let us choose a node that has no entering edge. If it is the initial node co then it will down propagate its initial information. From there, this node will interfere no more with the propagation in its subgraphs as it can neither propagate back on the same edge what it has just received nor propagate what it has received from one one edge onto the other as guards on leaving edges are exclusive. Remark 6.2. If L � is a superset of L such that L � \ L only contains inequalities between integers, then, for every vector terms co 1 and co 2 known in L and every pair (index, guards) ∈ eq L (i,co) (co 1 ) such that L � T guards, we have eq L � (index,co 1 ) (co 2 ) ⊆ eq L (i,co) (co 2 ). The sets of terms starting with element that can be created from a term element(co, i) ∈ T (L) is bounded by the set {element(co � , index) | co � ∈ T (L) and (index, guards) ∈ eq L (i,co) (co � )}. The proof relies on the fact that, for every modification term in L, we can only produce a new term element(co � , i � ) if we already know that the guard is true. This comes from the fact that our implementation uses a rather permissive notion of termination by requiring decisions to be done on the first element of a clause, as explained in Section 4.2.2. Thanks to this property, we have that we can only produce a term element(co � , i � ) using our axiomatization if there is a set of literals guards such that (i � , guards) ∈ eq L (i,co) (co � ) and L � T guards. The complete bound then comes from Remark 6.2.

Proof of Soundness

We show that, if a set of literals G has a model in the axiomatization without triggers and witnesses then there is a total model of G and the axiomatization. If I is a model of a set of literals G in the axiomatization without triggers and witnesses, we define L = {l | I(l) = �}. By construction of L, L is a total model of G. Since L is total, for every axiom ϕ of the axiomatization, L � T ϕ.

Proof of Completeness

We first need a lemma that states that equalities between integers can safely be added to partial models of the axiomatization: Lemma 6.1. If the axiomatization is true in a world L, t 1 ,t 2 ∈ T (L) have type integer and L � T t 1 � ≈ t 2 then the axiomatization is also true in L ∪ t 1 ≈ t 2 .

Proof. The proof is the same proof we did for Lemma 2.4

Let G be a set of literals and L a world in which G and the axiomatization are true. We construct a model from L in the axiomatization without triggers and witnesses. Since L � T G, it is also a model of G.

Since L is T -satisfiable, let I T be a model of L. No integer constant appears in a trigger of the axiomatization. As a consequence, the axiomatization is true in L ∪ {i ≈ i | i is an integer constant}. For every term t ∈ T (L) of the form length(co), to_index(cu), or capacity(co), we add t ≈ I T (t) to L. By Lemma 6.1, the axiomatization is still true in L.

For every term co of type vector in L, if length(co) (resp. capacity(co)) is not in T (L) modulo T , we add length(co) ≈ 0 (reps. capacity(co) ≈ 0 to L). This amounts to deciding that vectors that are not forced to be non-empty are empty. The axiomatization is still true in L. Indeed, the axiom LENGTH_RANGE creates a term capacity(co) for each term length(co) in L and TO_INDEX_TO_CURSOR creates a term length(co) for each term to_cursor(co, i) that is not no_element. In the same way, for each term cu of type cursor in L, if to_index(cu) is not in T (L) we can add to_index(cu) ≈ 0 to L.

We now need to associate a cursor to every position of every non-empty vector. For this, like for doubly-linked lists, we consider zones of vectors. For vectors it is simpler since cursors are not preserved when elements are shifted. We define a zone of a term co of type vector in L to be a pair of a vector and an index (co, i), with i in index_type__zero..length(co) + index_type__zero.

For every zone z of a vector, we define the equivalence class of z, written eq(z), to be the set of the zones that are bound to contain the same cursors as z by literals in L. For every (co, i) ∈ eq(z):

L � T insert(co 1 , j, co 2 , co) ≈ t and L � T index_type__zero < i < j → (co 1 , i) ∈ eq(z) L � T insert(co, j, co 2 , r) ≈ t and L � T index_type__zero < i < j → (r, i) ∈ eq(z) L � T delete(co 1 , j, l, co) ≈ t and L � T index_type__zero < i < j → (co 1 , i) ∈ eq(z) L � T delete(co, j, l, r) ≈ t and L � T index_type__zero < i < j → (r, i) ∈ eq(z) L � T delete_end(co 1 , j, co) ≈ t and L � T index_type__zero < i < j → (co 1 , i) ∈ eq(z) L � T delete_end(co, j, r) ≈ t and L � T index_type__zero < i < j → (r, i) ∈ eq(z) L � T replace_element(co 1 , j, e, co) ≈ t → (co 1 , i) ∈ eq(z) L � T replace_element(co, j, e, r) ≈ t → (r, i) ∈ eq(z) L � T swap(co 1 , i 1 , i 2 , co) ≈ t → (co 1 , i) ∈ eq(z) L � T swap(co, i 1 , i 2 , r) ≈ t → (r, i) ∈ eq(z) L � T equal_vectors(co 1 , co) ≈ t → (co 1 , i) ∈ eq(z) L � T equal_vectors(co, r) ≈ t → (r, i) ∈ eq(z)
It is straightforward to check that, if to_cursor(co, i) is not known in L then, for every pair (co � , i) ∈ eq(co, i), the term to_cursor(co � , i � ) is not known in L. If, for a vector co and an index index_type__zero < i ≤ length(co) + index_type__zero, the term to_cursor(co, i) is not known in L, for each zone (co � , i) ∈ eq(co, i), we add the literals to_cursor(co, i) ≈ to_cursor(co � , i), has_element(co � ,to_cursor(co � , i)) ≈ t, and to_index(to_cursor(co, i)) ≈ i to L. This cannot introduce an inconsistency since to_cursor(co � , i), and thus has_element(co � ,to_cursor(co � , i)), are not known in L. What is more, a quick survey of the axiomatization shows that the axiomatization is still true in L. After this step, there is a cursor per index in every vector in L, and the value of next, previous, first, and last can be set without introducing new terms nor contradictions.

Let us now consider the elements contained by the vector. Let e be a fresh term of type element. We map element(co, cu) to e for any term co of type vector and any term cu of type cursor in L such that element(co, cu) is not in L modulo T . Each axiom with element(co, cu) as a trigger either deduces an equality or an equivalence between new terms, or a non-equivalence between a known term and a new term. As a consequence, the axiomatization is still true in L.

Benchmarks

We have written solutions in SPARK 2014 for problems given in the verification competition held at the VSTTE conference (Verified Software: Theories, Tools, and Experiments) 1 . We try our mechanism on some of these solutions involving vectors:

• a two-way sorting algorithm for vectors of Booleans from VSTTE 2012. It is implemented using swap. Our implementation only solves partially the problem as its postcondition only states that the result of the function is sorted, not that it is a permutation of the input vector.

• an implementation that solves the n queens problem from VSTTE 2010 using a vector. The vector stores the positions of the queens on the board. We show both that, if a solution is found then it is indeed a solution and that, if no solution is found then there is no solution. This last property is expressed using an additional, unspecified vector argument to the procedure to model universal quantification over vectors.

• a ring buffer implementation in a vector of fixed size, from VSTTE 2012. This implementation is specified thanks to a model of the content of the ring buffer represented as a vector.

• a functional amortized queue implementation made of two vectors, from VSTTE 2010. The elements are dequeued at the end of the first vector and enqueued at the end of the second. If the second part becomes bigger than the first, then its elements are reversed and inserted at the beginning of the first vector. This implementation is also specified thanks to a model of the content of the queue represented as a vector.

The SPARK 2014 code for these examples is given in Appendix C.2.

Assessment of the Adequacy Between our Framework and Usual E-Matching Techniques

In Figure 6.1, we compare the results of the tests with and without the manually written triggers using both Alt-Ergo 0.95, CVC4 1.2, and Z3 3.2. The results are heterogeneous. First, we can see that, without the triggers, we are more incomplete. Alt-Ergo returns Unknown on several tests. Some tests take a lot more time to complete without the triggers, like the postcondition of the function returning the model of an amortized queue. We can also see that, in some cases, for example the postconditions of the Push function on ring buffers, Alt-Ergo does better without the triggers. It can be that it has less things to do because it is incomplete.

Comparison Between our Implementation and the Built-in Quantifiers Handling of Alt-Ergo

As in Section 4.2, we have two versions of our axiomatization of bounded vectors, one with separated and one with grouped axioms. The theory mechanism is useful in particular when the vector API is used a lot. It is the case for the ring buffer and the amortized queue. We can see this speedup in the postcondition of the function returning the model of the structure in particular. For the postcondition of the API function, this is less obvious since the results are polluted by the fact that the definition of the function returning the model is always handled outside the theory.

There are always a great deal of axioms with quantifiers that do not belong to the theory when verifying a SPARK program. Still, if these axioms do not break termination, which is the case for the definition of the two model functions, they can be included in the theory. For the last two axiomatizations, we redo the tests by adding a new axiom, specific to the use case, to describe how to get a model for the content of the structure.

As we see in Figure 6.3, this new axiom is helpful both for Alt-Ergo and Alt-Ergo � . Indeed, the manually added triggers are also useful for the general quantifier handling of Alt-Ergo. Still, it appears that eager instantiation gives better results. Indeed, the proofs of the postconditions of the API function now mostly use axioms from the theory.

Conclusion

Although eager instantiation results in a significant speedup for verification conditions requiring a lot of theory reasoning, results are overall less impressive than for the BWare verification conditions. Indeed, verification conditions coming from the verification of our SPARK 2014 programs involve a lot of first-order axioms that are not part of the theory, coming from the translation of integer ranges like in Section 6.1 for example, and require relatively little vector specific reasoning.

Alt

Conclusion

Summary of the Contributions

We have introduced a semantics for first-order logic with triggers and witnesses. Thanks to this semantics, we have defined three properties of soundness, completeness, and termination for first-order axiomatizations with triggers. We have then presented two implementations of SMT solvers that accept a first-order axiomatization of a theory T � and yield a decision procedure for T � if the axiomatization is sound, complete, and terminating in our framework. The first one uses a ground SMT solver as a black box (implemented in practice using Z3). Though the formalization is rather simple, it appears to be unpractical as it requires too costly communications with the underlying solver. The second implementation is formalized using an extension of the abstract DPLL(T ) framework and is integrated into the SMT solver Alt-Ergo's main algorithm.

Since we have demonstrated that, in this formalization, quantifier handling terminates for a terminating axiomatization, we have designed our implementation to instantiate eagerly quantifiers coming from the axiomatization of the theory, that is, create new instances as soon as they become possible, without waiting for all the literals to be assigned in the current truth assignment of DPLL(T ).

We have tested the white-box implementation on several case studies: hand-written Why3 programs using a theory for imperative doubly-linked lists with iterators, large benchmarks using a theory of sets coming from the verification of B programs, and SPARK programs inspired from the VSTTE competition using a library for bounded vectors. We have compared our implementation with the built-in quantifier handling of Alt-Ergo, which treats quantifiers lazily. As a result, it seems that eager instantiation can be really efficient when a lot of theory reasoning is needed. On the other hand, when the axiomatization of the theory is big, this eager instantiation becomes costly. It is all the more noticeable when the tests require a lot of instances of axioms outside the theory and relatively few theory reasoning, which is the case for our programs using SPARK vectors.

Since in off-the-shelf SMT solvers, triggers are a mere heuristic, they don't have a proper semantics and users are sometimes at a loss to find the proper triggers for their axiomatizations. We have compared the behavior of off-the-shelf SMT solvers that handle quantifiers on the axiomatizations used in our case studies with and without manually supplied triggers. The results show that our semantics can indeed help in the choice of these triggers. In particular, it can help understanding why an axiom may slow down a prover by inducing too many instances and what are the triggers that should be used to prevent this explosion. Still, it is not in general worthwhile to achieve completeness or termination of axiomatizations in this context as most SMT solvers use other instantiation heuristics than triggers.

Related Work

Instantiation with Triggers: Triggers are a commonly used heuristic in SMT solvers that handle quantifiers. User manuals of such solvers usually explain how they should be used to achieve the best performance. Triggers can be automatically computed by the solvers but it is commonly agreed that user guidance is useful in this domain [START_REF]Programming with triggers[END_REF]. We believe that our formalization along with the methods and advice in Section 2.3 give valuable guidance for this choice. A lot of work has also been done on defining an efficient mechanism for finding the instances allowed by a trigger. These techniques, called E-matching, are described for Simplify [START_REF] Detlefs | Simplify: a theorem prover for program checking[END_REF]58], Z3 [START_REF] De Moura | Efficient E-matching for SMT solvers[END_REF], and CVC3 [START_REF] Ge | Solving quantified verification conditions using satisfiability modulo theories[END_REF].

Triggers can also be used in semi-complete first-order ATPs to guide the proof search and improve the prover's efficiency. For example, the Princess [START_REF] Rümmer | E-matching with free variables[END_REF] prover that combines a complete calculus for first-order logic with a decision procedure for linear arithmetic can use triggers.

Other generic techniques for quantifier handling in SMT solvers: Model-based quantifier instantiation [START_REF] Ge | Complete instantiation for quantified formulas in satisfiabiliby modulo theories[END_REF] is an other heuristics for generating instances. This instantiation mechanism generates new instances when the solver is about to return Sat, that is, when it has computed a candidate model. A candidate model is not always a model when the solver is not in its domain of completeness. It is the case in particular when quantifiers are involved. Modelbased instantiation does not in general increase the solver's performance. On the other hand, it increases its accuracy since it allows to continue the search when otherwise the solver would have stopped with only a partial model. It goes through the candidate model M and searches for an instance that is not already implied by M. If one such instance is found, the search goes on.

A saturation process, close to the superposition calculus, has also been integrated into abstract DPLL(T) by de Moura and Bjørner [START_REF] De Moura | Engineering DPLL(T) + saturation[END_REF]. The idea is to add elements to the set of clauses that are handled by DPLL by using the superposition calculus. If superposition steps use the current partial assignment of DPLL, the inferred clauses must be prefixed by a guard so that they can be reversed when the current partial assignment is backtracked.

Specialized complete instantiation techniques:

In SMT solvers, the idea that a set of firstorder formulas can be saturated with a finite set of ground instances has been explored previously.

For example, decision procedures for universally quantified properties of functional programs can be designed using local model reasoning [START_REF] Jacobs | Towards complete reasoning about axiomatic specifications[END_REF]. This property states that, for every input problem, it is enough to instantiate universal quantifiers with terms from the problem. Like us, they work modulo an existing background theory and, like us, proofs of locality of axiomatizations are not in general automatic. They also describe some restricted forms of universally quantified formulas for which locality is automatic.

In the same way, Ge and de Moura [START_REF] Ge | Complete instantiation for quantified formulas in satisfiabiliby modulo theories[END_REF] describe fragments of first-order logic for which one can automatically compute a finite set of ground instances that are enough to ensure complete-ness. Like for [START_REF] Jacobs | Towards complete reasoning about axiomatic specifications[END_REF], the automation of the termination checks are at the expense of the range of application of these fragments which is much smaller than what we propose in our framework.

McPeak et al. also designed a procedure to decide a particular form of first-order formulas [START_REF] Mcpeak | Data structure specifications via local equality axioms[END_REF]. It is dedicated to descriptions of shape properties of data structures, involving pointers and predicates on scalar fields. This procedure is always terminating but is only complete for a more restricted form of formulas. Outside this restriction, they do not provide ways to check that a particular axiomatization is complete.

Our work is in this respect closer to the work done on the axioms for the theory of arrays. For this theory, a specialized instantiation technique achieves both termination and completeness [17,[START_REF] De Moura | Generalized, efficient array decision procedures[END_REF]. The idea is to treat operations over arrays as uninterpreted functions but to supply additional information about them, that is, instances of array axioms, at appropriate times. In a more general way, some SMT solvers accept sets of rewriting rules as input. Such a set of rules can also produce a decision procedure in specific cases [START_REF] Knuth | Simple word problems in universal algebras[END_REF].

Instantiation-based theorem prover such as I-prover [46] are semi-complete procedures for first-order logic based on instantiation [START_REF] Korovin | An invitation to instantiation-based reasoning: From theory to practice[END_REF]. To choose appropriate instances, they use a heuristic based on models. A ground solver is launched on the set of ground facts contains in the input problem. If a model is found, they use it to generate instances of universally quantified formulas from the problem that contradict this model. Unlike in our framework, no specific proof is required to insure the completeness of this procedure. On the other hand, this procedure only works modulo theories for which there is an "answer-complete" ground solver, that is, a solver able to find a most general substitution for ground instances of a set of literals with free variables to be unsatisfiable [START_REF] Ganzinger | Theory instantiation[END_REF]. For example, there can be no answer-complete solver for arithmetics.

Demonstrating that a solver always terminates on a given first-order axiomatization of a theory in order to obtain a decision procedure has been done by Armando et al. on a paramodulationbased procedure [2]. In this work, the termination of the superposition calculus on the conjunction of the axiomatization and any input problem was demonstrated manually.

Lynch et al. then extended this work by introducing automatic procedures for deciding the termination [START_REF] Lynch | Automatic decidability[END_REF][START_REF] Lynch | Automatic decidability and combinability[END_REF]. In particular, Schematic Saturation is used to over-approximate the inferences that paramodulation can generate while solving the satisfiability problem for a certain theory. As opposed to our work, they do not reason modulo a background theory. To allow combination with other decision procedures in a Nelson-Oppen framework, they explain how schematic saturation can also be used to check stably-infiniteness and complete deduction in some cases.

Other ways of integrating decision procedures in an SMT solver: Apart from first-order axiomatizations, there are several ways that can be attempted to add a new decision procedure into a SMT solver. Nikolaj Bjørner describes how several non-native theories where successfully supported using the SMT solver Z3 [START_REF] Bjørner | Engineering theories with Z3. Programming Languages and Systems[END_REF]. A rather usual way to support a new theory is to encode it in an already supported domain. This paper also presents an API that can be used to add decision procedure to Z3 using callbacks. Both systems require a lot of thinking to get an efficient design as well as manual proofs of completeness and termination, exactly like our axiomatization framework. We believe that each of these different techniques can be better suited than the others to specific theories.

Perspectives

Try our Framework on Other Classical Theories: A lot of different theories have been axiomatized in first-order logic to alleviate the lack of a specific decision procedure. It is the case for example of algebraic data-types and monoïds. It would be interesting to see if it can be decided using our approach.

Use Literals as Triggers: As discussed in Section 4.3, in our white-box implementation in Alt-Ergo (like in most SMT solvers) we only handle terms as triggers. It would be worthwhile to support literal triggers, as described in our theoretical framework, in particular to handle extensionality axioms.

Automatic Checking for Completeness and Termination: In Section 2.3, we have presented two automatable techniques for checking termination and completeness of an axiomatization with triggers. It would be interesting to implement them and to improve their range of application, as we believe they would be a valuable help for developers of new axiomatizations. Such an implementation could also be used to check triggers automatically computed by SMT solvers as part of the E-matching heuristic or even to automatically compute triggers for a given axiomatization.

Automatic Generation of Complete Triggers: E-matching, that is, instantiation with triggers is the most popular heuristic to handle first-order logic in SMT solvers. The manual annotation of every universal quantifier with triggers is not usually required though. These solvers rather use heuristics to automatically choose triggers for quantified formula. One of the most common heuristic consists in choosing one or several terms from the formula. We could use our framework to characterize formulas for which a specific heuristic for choosing triggers always succeeds in yielding a complete axiomatization.

Learn Conflict Clauses in DPLL � (T): The DPLL � framework described in Chapter 4 does not allow learning of conflict clauses. As the ability to keep clauses that have lead to previous conflicts is one of the keys for efficiency of modern SAT and SMT solvers, we would like to extend our framework to allow it. In particular, as we notice in Remark 4.3, that would require extending our logic of guarded clauses to allow negations of closures in theory clauses.

Adapt Triggers to Reasoning Modulo Theory: In our framework, to instantiate a formula protected by a trigger l, we need l to be true and all its subterms to be known. When l contains interpreted subterms, in particular integer constants or operations, this may not be the appropriate behavior. For example, if l is x > 0, we may not want to ask for 0 to be known for l to be unfolded. To improve our semantics, we could group some interpreted subterms together and not require the presence of interpreted subterms of interpreted terms of l.

Combine Axiomatized Theories: In the longer term, we would like to investigate the combination of several theories defined as first-order axiomatizations. Indeed, if we want to add several theories using our framework, then we cannot do the proofs of completeness and termination modularly. In particular, we will need to determine which requirements are needed to preserve completeness and termination of axiomatizations. In a first approximation, we expect that a combination of two terminating axiomatizations W 1 and W 2 is terminating whenever triggers from W 2 only contain uninterpreted function symbols that do not appear in W 1 . In the same way, we expect that the combination of two complete axiomatizations W 1 and W 2 is complete if W 2 can only deduce equalities between terms whose head symbol does not appear in W 1 . More fine-grain combinations would require more thought.

Use Triggers in Other Automatic Theorem Provers: We could reuse our mechanism for triggers in other solvers than SMT. We could for example extend resolution and paramodulation with triggers to restrict the search space. An idea could be to consider triggers as guards and to only allow resolution and paramodulation inside the clause if all the guards have been removed. In other words, we would have to paramodulate away guards before accessing the content of a clause. We could try to prove that this restriction preserves semi-completeness on a complete axiomatization following Definition 2.7.

Consider Theories not Efficiently Axiomatized: There are theories on which our approach cannot be used as they have no practical first-order axiomatization with triggers. First, too many instances may be required to ensure completeness. It is the case for example of the interval(i, j) function of the theory of sets that returns the set containing every integer between i and j. To be complete, we need to produce mem(k, interval(i, j)) ≈ t for every integer k between i and j. Then, for some theories, there can be no efficient decision procedure based on instantiation, like for linear arithmetics. Finally, in some cases, triggers are not good hints to restrict instantiation of a formula. For example, in axioms for associativity and commutativity, the only triggers that ensure completeness are those that allow every equivalent form of an expression to be deduced. Efficient approaches for handling these axioms are based on normalization of terms via flattening [23].

Improve Provability in the SPARK 2014 Tool: Our theory mechanism is not included in the SPARK 2014 tool for now. Indeed, as we have seen in Chapter 6, it is rarely the case that the verification of a SPARK 2014 program heavily relies on data-structure reasoning. Even when a program requires several updates to a structure, they are generally done using a loop and therefore only one update at a time is seen by the tool. Still, a user may benefit from our mechanism to discharge only the verification conditions requiring a lot of theory reasoning, as an alternative solver. We could also experiment on other theories to see if we have a better gain.

Unless explicitly specified otherwise, the SMT solver Alt-Ergo is the only solver used in the SPARK 2014 tool. While using the tool we have witnessed several bottlenecks. First, linear arithmetics in Alt-Ergo is handled by a Fourier-Motzkin based approach which is significantly less efficient when there are a lot of inequalities in the problem. As Ada programs generally use lots of specific integer types with a precise range, the SPARK 2014 tool often runs into this problem. Not surprisingly, the other big issue is quantifiers. Indeed, even verification conditions coming from a simple SPARK 2014 program like the Max _ Array function introduced in Section 1.1.1 include more than one hundred universal quantifiers (170 for the postcondition of Instantiation tree representation of any possible run of the solver using a particular instantiation strategy. 24

Pseudo-clause disjunctive set of pseudo-literals. 23

Pseudo-literal either a literal or a universally quantified formula, a trigger, or a witness over a pseudo-clause. 23

Super-clause either a user clause or a theory clause. 59

A Imperative Doubly-Linked Lists ∀co 1 , co 2 : vector, i, l, j : int.[delete(co 1 , i, l, co 2 ), element(co 1 , j)] delete(co 1 , i, l, co 2 ) ≈ t → index_type__zero < j < i → element(co 1 , j) ≈ element(co 2 , j) ∀co 1 , co 2 : vector, i, l, j : int.[delete(co 1 , i, l, co 2 ), element(co 2 , j)] delete(co 1 , i, l, co 2 ) ≈ t → index_type__zero < j < i → element(co 1 , j) ≈ element(co 2 , j) DELETE_ELEMENT_2 : ∀co 1 , co 2 : vector, i, l, j : int.[delete(co 1 , i, l, co 2 ), element(co 1 , j)] delete(co 1 , i, l, co 2 ) ≈ t → i + l ≤ j ≤ length(co 1 ) + index_type__zero → �i + l� element(co 1 , j) ≈ element(co 2 , jl) ∀co 1 , co 2 : vector, i, l, j : int.[delete(co 1 , i, l, co 2 ), element(co 2 , j)] delete(co 1 , i, l, co 2 ) ≈ t → i ≤ j ≤ length(co 2 ) + index_type__zero → element(co 1 , j + l) ≈ element(co 2 , j)

DELETE_CURSORS:
∀co 1 , co 2 : vector, i, l, j : int.[delete(co 1 , i, l, co 2 ),to_cursor(co 1 , j)] delete(co 1 , i, l, co 2 ) ≈ t → index_type__zero < j < i → to_cursor(co 1 , j) ≈ to_cursor(co 2 , j) ∀co 1 , co 2 : vector, i, l, j : int.[delete(co 1 , i, l, co 2 ),to_cursor(co 2 , j)] delete(co 1 , i, l, co 2 ) ≈ t → index_type__zero < j < i → to_cursor(co 1 , j) ≈ to_cursor(co 2 , j) DELETE_END_RANGE: ∀co 1 , co 2 : vector, i : int.[delete_end(co 1 , i, co 2 )] delete_end(co 1 , i, co 2 ) ≈ t → index_type__zero < i ≤ length(co 1 ) + index_type__zero DELETE_END_LENGTH: ∀co 1 , co 2 : vector, i : int.[delete_end(co 1 , i, co 2 )] delete_end(co 1 , i, co 2 ) ≈ t → length(co 2 ) ≈ i -index_type__ f irst DELETE_END_CAPACITY: ∀co 1 , co 2 : vector, i : int.[delete_end(co 1 , i, co 2 )] delete_end(co 1 , i, co 2 ) ≈ t → capacity(co 1 ) ≈ capacity(co 2 ) DELETE_END_ELEMENT: ∀co 1 , co 2 : vector, i j : int.[delete_end(co 1 , i, co 2 ), element(co 1 , j)] delete_end(co 1 , i, co 2 ) ≈ t → index_type__zero < j < i → element(co 1 , j) ≈ element(co 2 , j) ∀co 1 , co 2 : vector, i j : int.[delete_end(co 1 , i, co 2 ), element(co 2 , j)] delete_end(co 1 , i, co 2 ) ≈ t → index_type__zero < j < i → element(co 1 , j) ≈ element(co 2 , j)

Figure 1 . 1 :

 11 Figure 1.1: Deductive verification in SPARK 2014

Figure 1 . 2 :

 12 Figure 1.2: Formal verification of the Max _ Array function

Figure 1 . 3 :

 13 Figure 1.3: Formal verification of the Max _ Array function with a loop invariant

Theorem 2 . 3 .

 23 If� ϕ∈W N W (ϕ, ∅) does not contain any newly introduced constant then W is terminating.

Example 2 . 4 .

 24 Let us consider the theory W array of non-extensional arrays defined in Example 1.1.

Figure 3 . 1 :

 31 Figure 3.1: Deduction rules used to compute new guarded clauses from the set of relevant closures L| R in a model L of �R�.

  Since we start with (W ∪ G) • ∅, guarded clauses with non-empty substitutions in closures may only be added via deduction rules in Figure 3.1.

Theorem 3 . 1 (

 31 Soundness). If L ift(S) returns Unsat on a set of ground formulas G and a sound axiomatization Ax of T � then G is unsatisfiable modulo T � .

Theorem 3 . 2 (

 32 Completeness). If L ift(S) returns Sat on a set of ground formulas G and a complete axiomatization Ax of T � then G has a model modulo T � .

scoped _ updates: n = 1 Figure 3 . 2 :

 132 Figure 3.2: Time (in seconds) needed to solve tests for arrays with our implementation

Figure 3 . 3 :

 33 Figure 3.3: Time (in seconds) needed to solve tests for lists with our implementation

Definition 4 . 1 (

 41 Truth value). Given a world L, we define what it means for a super-literal, a

Lemma 4 . 1 .

 41 Let S be a conjunctive set of super-literals and/or guarded clauses and let E be a super-literal, a super-clause, or a guarded clause such that S � � T E. Then every model of S is a model of E.

Lemma 4 . 2 .

 42 Let C be a super-clause and F be a set of super-clauses such that F � � T C. For every world L such that L � F, L � C.Proof. We have four cases to consider. Assume that C is a unit user clause and LIT(F) ∪ �CLO(F)� � T C. Since L � F, L � T LIT(F) and L � T �CLO(F)�. As a consequence, L � T C.The case where C is a non-unit user clause is handled in the same way.

Lemma 4 . 3 .

 43 Let C be a super-clause and F be a set of super-clauses such that F � � T C. We have F � � T C. Proof. Let L be a model of �F�. We have four cases to consider. Assume that C is a unit user clause and LIT(F) ∪ �CLO(F)� � T C. Since L � T �F�, L � T LIT(F) and L � T �CLO(F)�. As a consequence, L � T C. The case where C is a non-unit user clause is handled similarly. Otherwise, C is a theory clause D • σ . Assume that there is l ∈ D such that F � � T lσ and F ∪ known(T (CLO(F))) � � T known(T (lσ )). Since L � T �F�, L � T LIT(F) ∪ �CLO(F)�. As a consequence, L � T lσ and L � T �C�.

Lemma 4 . 4 .

 44 Let C be a super-clause and F 1 and F 2 be two sets of super-clauses

Lemma 4 . 5 .

 45 Let M be a set of super-literals and e a super-literal. If �M� � � T e then M � � T e.

  undefined in M, and e � or ¬e � occurs in AVB(F, M) ∪ LIT(Me d N)

Figure 4 . 1 :

 41 Figure 4.1: Transition rules of DPLL � (T ) on guarded clauses

Figure 4 . 2 :

 42 Figure 4.2: Additional transition rules for Abstract DPLL � (T ) on guarded clauses

  In this section, we motivate the constraints on the rules T -Propagate, T -Backjump, T -Learn, T -Forget, and Instantiate using examples. These constraints are closely related to the definition of termination in Section 2.1.4. They aim at forbidding: • The addition into M of a super-literal that should be protected by a trigger. It requires keeping track of guards that should be protecting a new clause when learning it. This idea motivates the constraints on T -Propagate, T -Backjump, and T -Learn.

Theorem 4 . 1 (

 41 T -Soundness). If the solver returns Unsat on a set of user clauses G with a sound axiomatization Ax of an extension T � of T then G has no model in the theory T � .

  First note that, by Lemma 4.6, L � F. For the rule UnitPropagate, L � H → C ∨ e and L � H ∪ ¬C. By definition of �, L � e. For the rule T -Propagate, M � � T e and, since L is complete, L � e by Lemma 4.1. The only remaining rule is T -Backjump. There is a subset D of M such that F ∪ D � � T e. Since L � M and L � F, since L is complete, L � e by Lemma 4.1. Thanks to the previous lemma, we can perform the proof of Theorem 4.1. If the solver returns Unsat on G with W then there is a derivation ∅ � G ∪W • ∅ =⇒ � M � F, H → C =⇒ fail such that M contains no decision literals and H ∧ ¬C ⊆ M. By contradiction, assume G has a model in T � . There is a T � -satisfiable set of literals G � such that G � � T G. Since Ax is sound, W ∧ G � has a model L. By Lemma 4.8, L � M. What is more, by Lemma 4.6, L � F, H → C. With H ∧ ¬C ⊆ M, we get a contradiction.

Theorem 4 . 2

 42 (T -Completeness). If the solver returns Sat on a set of clauses G with a complete axiomatization Ax of T � then G is T � -satisfiable.

•

  [l]C • σ ∈ M. Assume L � lσ . By definition of �, we have both M � � T lσ and M ∪ known(T (M)) � � T known(T (lσ )). Thus �M� � � T l • σ . By (iii), AVB(F, M) � � T C • σ . As a consequence, L � Cσ like in the two previous cases and, by definition of �, L � ([l]C)σ .

  b) for every unit super-clause e ∈ AVB(F, ∅), we have �N� � � T e, (c) for every closure �l�C

Lemma 4 . 10 .

 410 For every closure ϕ • σ such that �N� � � T ϕ • σ , there is a truth assignment A of A M ∪ ϕ • σ such that �N� � � T UNIT(A).

Corollary 4 . 1 .

 41 For every guarded clause H → C ∨ ϕ • σ that can be obtained by applying either Witness-Unfold or Trigger-Unfold such that ϕ

Lemma 4 . 11 (

 411 Conflict Analysis). If there is a conflict clause in the state M � F and M contains at least a decision literal, then there is a possible application M � F =⇒ M � e � F of T -Backjump.

Theorem 4 . 4 (

 44 Progress). If the solver can not return after a fair derivation ∅ � G ∪W • ∅ =⇒ M � F, then there is a fair derivation M � F =⇒ + S containing no Restart step and at least one step that is neither an application of T -Learn or T -Forget nor a redundant application of Witness-Unfold, Trigger-Unfold or Instantiate.

  and, by Lemma 4.11, either Fail or T -Backjump can be applied. Otherwise, there is an undefined super-literal e that occurs in AVB(F, M). Since e ∈ AVB(F, M), level M (e) ≤ level(M) and Decide can be applied on e. If (ii) is false, then LIT(M) ∪ {lσ | l • σ ∈ M} � T ⊥. Like in the proof of Corollary 4.2, a conflict clause can be learned so that either Fail or T -Backjump can be applied. If (iii) is false in M � F, there is a guarded clause H → C that can be added to the set F using either Instantiate, Witness-Unfold, or Trigger-Unfold on M such that either AVB(F, H) � � � T C or we have AVB(F, H) � � T C and H � M. If AVB(F, H) � � � T C, this application of Instantiate, Witness-Unfold, or Trigger-Unfold is non-redundant in F. Otherwise, �M� � � T H and, for some l •σ ∈ H \M, l •σ ∈ GRD(F). If ¬(l •σ ) ∈ M, we conclude using Corollary 4.2. Otherwise, T -Propagate can be applied to l • σ if it is not forbidden by fairness. Assume the application of T -Propagate is forbidden by fairness. The application adding H → C to F must be an application of Instantiate and l • σ must be of the form x ≈ x • [x � → t]. At least one of the following properties is false: (a) M � � � T ⊥, (b) for every unit super-clause e ∈ AVB(F, ∅), �M� � � T e, (c) for every closure �l�C

  added to the assignment. A compliant implementation could be obtained by requiring an eager application of T -Propagate and UnitPropagate in clauses.

Figure 4 . 3 :

 43 Figure 4.3: Time (in seconds) needed to solve all tests with Alt-Ergo giving the first-order axiomatization directly and Alt-Ergo through the theory mechanism (named Alt-Ergo � ).

Figure 4 .

 4 Figure 4.3 is a comparison between the results obtained by giving the axiomatization directly in input to Alt-Ergo and using it as a theory through the new mechanism. We use two slightly different versions of the axiomatization. The first one, named Separate Axioms is the one presented in Section 2.2. In the second one, we have grouped the axioms for each function symbol into one axiom. For example, for delete, we now have only one axiom:

Figure 5 . 2 :

 52 Figure 5.2: Evolution of the number of verification conditions from the BWare benchmarks without additional extensionality lemmas discharged by Alt-Ergo, Z3 4.3.2, and CVC4 1.1 with a timeout of 60s with and without the triggers and by Alt-Ergo through the theory mechanism.

Figure 5 . 3 :Figure 5 . 4 :

 5354 Figure 5.3: Evolution of the number of verification conditions from the BWare benchmarks with additional extensionality lemmas discharged by Alt-Ergo 0.95.2, Z3 4.3.2, and CVC4 1.1 with a timeout of 60s with and without the triggers and by Alt-Ergo through the theory mechanism.

(*

  Program function to check G's body and contract * ) let def (void _ param : unit) ensures { r _ inv x = True /\ f _ post result x = True } = ( * Execute G's precondition to check there is no runtime error. Forget all about it afterward. * ) abstract ( _ ignore (r _ inv x && f _ pre x)); ( * Assume the precondition * ) any unit ensures { r _ inv x = True /\ f _ pre x = True }; ( * Translate G's body, use exception to handle return statement * ) try (result := f x; raise Return __ exc) with Return __ exc -> ( * Execute G's postcondition to check there is no runtime error. Forget all about it afterward. * ) abstract ( _ ignore (r _ inv x && f _ post !result x)); ( * Return result * ) !result end

type

  Elements _ Array is array (Count _ Type range <>) of Element _ Type; type Vector (Capacity : Capacity _ Range) is record Elements : Elements _ Array (1 .. Capacity); Last : Extended _ Index := No _ Index; end record; type Cursor is record Valid : Boolean := True; Index : Index _ Type := Index _ Type'First; end record;

  PREVIOUS_IN:∀co : vector, cu : cursor.[previous(co, cu)](length(co) + index_type__zero ≥ to_index(cu) > index_type__ f irst ∨ length(co) + index_type__zero > to_index(previous(co, cu)) > index_type__zero) → to_cursor(co,to_index(cu) -1) ≈ previous(co,cu) PREVIOUS_EXT: ∀co : vector, cu : cursor.[previous(co, cu)]

GlossaryT 16 L

 16 (t) set of all the subterms of a term t. � ϕ the formula with triggers ϕ is true in the world L. 18 �ϕ� is the formula with triggers ϕ where triggers have been replaced with implications and witnesses with conjunctions. 19�V � is {lσ | l • σ ∈ V } for any set of theory clause V . 24 L � H → C the guarded formula H → C is true in the world L. 60�H → C� is the encoding of the guarded formula H → C into a user clause, replacing triggers, witnesses, and universally quantified formulas with fresh predicate symbols. 46, 60�M� is M ∪ {l • ∅ | l ∈ LIT(M)}for any set of super-literals M. 63 F � � T C the super-clause C is redundant in the set of super-clauses F, that is, the behavior of the solver will be the same with and without it.. 61 S � � T E �S� entails �E� modulo the theory T , for any conjunctive set of super-literals and guarded clauses S and any super-literal of guarded clause E. 60 Anti-closure the negation of a closure ¬(ϕ • σ ). 59 Closure pair of a pseudo-literal and a substitution ϕ • σ . 24 Guarded clause a pair H → C of a conjunctive set of closures H and a super-clause C. 46, 59

  Context: Deductive Verification of Programs . . . . . . . . . . . . . . . . 1 1.1.1 The Ada 2012 and SPARK 2014 Languages . . . . . . . . . . . . . . 2 1.1.2 Deductive Verification in SPARK 2014 . . . . . . . . . . . . . . . . 3 1.1.3 Challenges in Deductive Verification of Programs . . . . . . . . . . . 5 1.2 Landscape in Deductive Verification of Programs . . . . . . . . . . . . . . 6 1.2.1 Deductive Verification Tools for Mainstream Languages . . . . . . . 6 1.2.2 Verification Condition Generation . . . . . . . . . . . . . . . . . . . 7 1.2.3 Automatic Theorem Provers and SMT Solvers . . . . . . . . . . . .

	1 Introduction
	Contents
	1.1

8 1.3 Problem and Contributions . . . . . . . . . . . . . . . . . . . . . . . . . . 10 1.3.1 First Order Axiomatizations as Decision Procedures . . . . . . . . . 10 1.3.2 Overview of the Contributions . . . . . . . . . . . . . . . . . . . . . 11 1.1 Context: Deductive Verification of Programs

  Formalization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 2.1.1 Preliminary Notions . . . . . . . . . . . . . . . . . . . . . . . . . . 16

	2 First-Order Logic with Triggers
	Contents
	2.1

2.1.2 Logic with Triggers (Syntax and Semantics) . . . . . . . . . . . . . . 17 2.1.3 Relation with Traditional First-Order Logic . . . . . . . . . . . . . . 18 2.1.4 Soundness and Completeness . . . . . . . . . . . . . . . . . . . . . 21 2.1.5 Termination . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 2.2 Case Study: Imperative Doubly-Linked Lists . . . . . . . . . . . . . . . . 26 2.2.1 Presentation of the Theory . . . . . . . . . . . . . . . . . . . . . . . 27 2.2.2 Description of the Axiomatization . . . . . . . . . . . . . . . . . . . 28 2.2.3 Proofs of Soundness, Completeness, and Termination . . . . . . . . . 30 2.2.4 Assessment of Adequacy with Respect to Existing Trigger Heuristics 33 2.3 Designing Terminating and Complete Axiomatizations . . . . . . . . . . . 35 2.3.1 Proving Termination of an Axiomatization . . . . . . . . . . . . . . 35 2.3.2 Designing a Complete Axiomatization . . . . . . . . . . . . . . . . . 40 2.3.3 An Automatable Debugger for Completeness . . . . . . . . . . . . . 42 2.4 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43

  The iterative part of an imperative list co is modeled by an integer length(co : list) : int representing the length of the sequence together with a total function position(co : list, cu : cursor) : int so that, for every cursor cu, position(co, cu) returns the position of cu in co if it appears in the sequence and 0 otherwise. The content part of co is modeled by a function element(co : list, cu : cursor) : element_type so that element(co, cu) returns the element associated to cu in co if any:

	elements : �	�		�
		↑	↑	. . .	↑
	cursors :	•	•		•
	positions : 1	2	length
	Thanks to this description, we can define several other functions. has_element(co : list, cu :
	cursor) : bool returns true if and only if cu appears in the iterative part of co and is_empty(co :
	list) : bool returns true if co is an empty list. The functions last(co : list) : cursor, f irst(co :
	list) : cursor, previous(co : list, cu : cursor) : cursor and next(co : list, cu : cursor) : cursor are
	used to iterate through the iterative part of co. If co is empty, last(co) and f irst(co) return a
	special cursor named no_element that never appears in any list. no_element is also added at
	both ends of the iterative part of co so that previous(co, f irst(co)), previous(co, no_element),
	next(co, last(co)) and next(co, no_element) are no_element:

  Time (in seconds) needed to answer correctly on all tests with a timeout of 1000s with and without triggers in the axiomatization. Bold figures point to notably better results for each prover. We do the tests with Alt-Ergo 0.95.2, Z3 3.2, and CVC4 1.2.

		Alt-Ergo 0.95.2	Z3 3.2	CVC4 1.2
	With Triggers	Yes	No	Yes	No	Yes	No
	test _ false	0.00 135.06	TO	TO	TO	TO
	test _ delete	3.04 145.60	0.03	0.06	0.86	TO
	test _ insert	34.31	TO	0.07	0.08	3.9	TO
	double _ size	13.67	TO	11.17	TO	13.3	TO
	filter	12.24	TO	0.56	TO	27.05	TO
	my _ contain	0.76	TO	0.11 880.05	0.65	449.31
	my _ find	7.46	TO	TO	TO	9.33	TO
	map _ f	5.68	TO	10.42	TO	Error	TO
	Figure 2.1:

val element (co:list) (cu:cursor) : element _ type requires { has _ element co cu } ensures { result = element co cu } val insert (co:ref list) (cu:cursor) (e:element _ type) : unit requires

  Description . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45 3.1.1 Preliminaries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45

	3 A Black-Box Decision Procedure
	Contents
	3.1

3.1.2 Deduction Rules for First-Order Logic with Triggers . . . . . . . . . 47 3.1.3 Soundness, Completeness, and Termination . . . . . . . . . . . . . . 48 3.2 Implementation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53 3.2.1 Description . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53 3.2.2 Benchmarks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54 3.3 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56

  Description . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58

	4 A White-Box Decision Procedure
	Contents
	4.1

4.1.1 Preliminaries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58 4.1.2 Description of DPLL(T) with triggers . . . . . . . . . . . . . . . . . 63 4.1.3 Termination Related Constraints . . . . . . . . . . . . . . . . . . . . 66 4.1.4 Soundness and Completeness . . . . . . . . . . . . . . . . . . . . . 68 4.1.5 Progress and Termination . . . . . . . . . . . . . . . . . . . . . . . 70 4.2 Implementation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80 4.2.1 E-Matching on Uninterpreted Sub-Terms . . . . . . . . . . . . . . . 80 4.2.2 Different Notions of Termination . . . . . . . . . . . . . . . . . . . 82 4.2.3 Inclusion into the Theory Combination Mechanism . . . . . . . . . . 83 4.2.4 Comparison with Alt-Ergo's Built-In Quantifier Handling . . . . . . 83 4.3 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85

  Remark 4.1. When there are no closures involved, the calculus above coincides with classical abstract DPLL modulo theories as long as unit clauses are only forgotten if they are implied by unit clauses. As a consequence, the changes in abstract DPLL can be implemented as an extension outside an existing DPLL implementation. Remark 4.2. The relation � � T on guarded clauses cannot be computed inside the solver, but it is not needed to implement DPLL � . Indeed, like in classical abstract DPLL(T ), conflict analysis allows to deduce enough applications of T -Backjump and T -Learn to ensure progress. This is explained below in Lemma 4.11 and Corollary 4.2. Remark 4.3. In classical abstract DPLL modulo theories, conflict driven lemmas, namely formulas allowing to deduce the added element e � in M after an application Me d N � F =⇒ Me � � F of T -Backjump, can be added to F using T -Learn. In our framework, this is not the case if e � is an anti-closure since super-clauses cannot contain anti-closures. This restriction can be removed by allowing to deduce guarded clauses H → C such that F, H � � T C where C may contain super-literals of all three kinds: literals, closures, and anti-closures. With this modification, if there is D ⊆ M such that F, D � � T e � and e � or ¬e � occurs in AVB(F, M) ∪ LIT(Me d N), CLO(M) → {¬e | e is an anti-closure or a literal of D} ∨ e � can be added to F using T -Learn.

	and
	(iii) if H → C can be added by either Instantiate, Witness-Unfold, or Trigger-Unfold then
	AVB(F, M) � � T C.

  and (iii) if H → C can be added by either Instantiate, Witness-Unfold, or Trigger-Unfold then AVB

  1 =⇒ S i =⇒ . . . =⇒ S j =⇒ . . . =⇒ S k where the only three Restart steps are the ones producing S i , S j and S Restart steps and more than n steps that are neither T -Learn or T -Forget steps nor redundant applications of Witness-Unfold, Trigger-Unfold or Instantiate. With the two following properties, we reach a contradiction: (i) Let M W be a finite set of super-literals. There is an integer max step such that, for every subderivation Der � of Der containing no Restart, if, for every state M � F in Der � , M ⊆ M W , then Der � contains no more than max step steps that are neither T -Learn, or T -Forget steps nor redundant applications of Witness-Unfold, Trigger-Unfold, or Instantiate. (ii) If W is terminating, then there is a finite set of super-literals M W such that, at every state M � F in Der, M ⊆ M W . Proof of (i): Let Der � be a sub-derivation of Der containing no Restart such that, for every state M � F in Der � , M ⊆ M W . We first need an order on partial models M. Every partial model M can be written M 1 e d 1 M 2 . . . M n e d n M n+1 where e d 1 . . . e d n are the only decision super-literals in M. The order is defined as the lexicographic order on sequences �M 1 . . . �M n+1 where �M k is the length of M k . An inspection of UnitPropagate, Decide, T -Propagate, and T -Backjump shows that they produce a strictly greater partial model. The other rules do not change the partial model. Since M W is finite and a partial model cannot contain the same super-literal twice, the size of strictly increasing sequences of partial models is bounded. As a consequence, we only have to consider sub-derivations that only consist of T -Learn, T -Forget, Instantiate, Witness-Unfold, and Trigger-Unfold steps. Since M W is finite, there can only be a finite number of distinct applications of Instantiate, Witness-Unfold, and Trigger-Unfold in the derivation. Therefore, if �CLO(M W ) is the number of closures in M W , there can only be �CLO(M W ) non-redundant applications of Instantiate, Witness-Unfold, and Trigger-Unfold in our sub-derivation. As a conclusion, there is an integer max step such that every derivation Der � contains no more than max step steps that are neither T -Learn or T -Forget steps nor redundant applications of Witness-Unfold, Trigger-Unfold or Instantiate.

k , either: there are more DPLL � steps that are neither T -Learn or T -Forget steps nor redundant applications of Witness-Unfold, Trigger-Unfold or Instantiate in S j =⇒ . . . =⇒ S k than in S i =⇒ . . . =⇒ S j , or a guarded clause including only literals and closures with empty substitutions is learned in S j =⇒ . . . =⇒ S k and is not forgotten in Der. Remark 4.6. If the axiomatization W is empty, those are exactly the restrictions needed for termination of classical abstract DPLL modulo theories. Proof. Assume that there is an infinite derivation Der that satisfies these restrictions. Since there is only a finite number of literals and closures with empty substitutions in G ∪W • ∅, after a finite number of steps, Restart steps in Der are separated by an increasing number of steps that are neither T -Learn or T -Forget steps nor redundant applications of Witness-Unfold, Trigger-Unfold or Instantiate. Since there is no infinite sub-derivation of Der made only of applications of T -Learn and T -Forget as well as redundant applications of Witness-Unfold, Trigger-Unfold, and Instantiate steps, for every integer n, there is a sub-derivation of Der containing no

  and(f) for every guarded clause H → C that can be added to the set F by applying Instantiate, Witness-Unfold or Trigger-Unfold on a closure of M � , if level M

  Context: the B Method . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87 5.2 A Decision Procedure for Why3's Sets . . . . . . . . . . . . . . . . . . . . 88 5.2.1 Presentation of the Theory . . . . . . . . . . . . . . . . . . . . . . . 88 5.2.2 Description of the Axiomatization . . . . . . . . . . . . . . . . . . . 89 5.2.3 Proofs of Soundness, Completeness, and Termination . . . . . . . . . 90 5.3 Benchmarks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92 5.3.1 Assessment of the Adequacy Between our Framework and Usual Ematching Techniques . . . . . . . . . . . . . . . . . . . . . . . . . . 93 5.3.2 Comparison Between our Implementation and the Built-in Quanti-

	5 Case Study: Set Theory of Why3
	Contents
	5.1

fiers Handling of Alt-Ergo . . . . . . . . . . . . . . . . . . . . . . . 96 5.4 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96

  Assessment of the Adequacy Between our Framework and Usual E-matching TechniquesIn Figure5.1, we compare the number of verification conditions discharged by Alt-Ergo 0.95.2, Z3 4.3.2, and CVC4 1.1 with and without user provided triggers in the axiomatization. There are two versions of the benchmarks, one includes additional lemmas for extensionality and the other does not. Figures 5.3 and 5.2 represents the time needed to solve the n fastest verification conditions with each prover, depending on n.

					lemmas
		No trigger With triggers No trigger With triggers
	Z3 4.3.1	8994	8586	8495	8649
	CVC4 1.1	8763	8768	8774	8781
	Alt-Ergo	9726	9761	9381	9662
	Figure 5.1: Number of verification conditions from the BWare benchmarks discharged by Alt-
	Ergo, Z3 4.3.2, and CVC4 1.1 with a timeout of 60s with and without the triggers.
	5.3.1

  Context: the SPARK 2014 Tool . . . . . . . . . . . . . . . . . . . . . . . . 97 6.2 Ada's Formal Vectors Package . . . . . . . . . . . . . . . . . . . . . . . . 99 6.2.1 Description of the Ada Package . . . . . . . . . . . . . . . . . . . . 99 6.2.2 Translation into WhyML . . . . . . . . . . . . . . . . . . . . . . . . 101 6.3 A Decision Procedure for Formal Vectors . . . . . . . . . . . . . . . . . . 101 6.3.1 Presentation of the Theory . . . . . . . . . . . . . . . . . . . . . . . 102 6.3.2 Description of the Axiomatization . . . . . . . . . . . . . . . . . . . 103 6.3.3 Proofs of Soundness, Completeness, and Termination . . . . . . . . . 107 6.4 Benchmarks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111 6.4.1 Assessment of the Adequacy Between our Framework and Usual E-Matching Techniques . . . . . . . . . . . . . . . . . . . . . . . . . . 112 6.4.2 Comparison Between our Implementation and the Built-in Quantifiers Handling of Alt-Ergo . . . . . . . . . . . . . . . . . . . . . . . 112 6.5 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112

	6 Case Study: Formal Bounded Vectors of SPARK
	2014
	Contents
	6.1

  Index _ Type is range <>; type Element _ Type is private; with function "=" (Left, Right : Element _ Type) return Boolean is <>;

generic type

  If Index is not between First _ Index (Container) and Last_Index (Container) + 1, Delete propagates Constraint _ Error. If Count is 0, Delete has no effect. Otherwise, Delete slides the elements starting at position Index + Count down to Index. Any exception raised during element assignment is propagated.

	procedure Delete (Container : in out Vector;
	Index	: in	Extended _ Index;
	Count	: in	Count _ Type := 1)
	with
	Pre => First _ Index (Container) <= Index
	and then Index <= Last _ Index (Container) + 1;

  Figure 6.2: Time (in seconds) needed to solve all tests with Alt-Ergo with a timeout of 3600s giving the first-order axiomatization directly and Alt-Ergo through the theory mechanism (named Alt-Ergo � ). Here we only list the verification conditions that are not immediately discharged by both provers. Figure6.3: Time (in seconds) needed to solve the two last tests with Alt-Ergo with a timeout of 3600s giving the first-order axiomatization directly and Alt-Ergo through the theory mechanism (named Alt-Ergo � ) using an additional, test specific axiom. Here we only list the verification conditions that are not immediately discharged by both provers.

			-Ergo	Z3 3.2	CVC4 1.2
	With Triggers	Yes	No	Yes	No	Yes	No
	two way sort: loop-invariant postcondition two way sort: n queens: loop-invariant in Copy_Until loop-invariant loop-invariant in Add_Next postcondition Try_Row loop-invariant n queens: 1 st call to Add_Next in Try_Row loop-invariant in Add_Next 4.50 54.06 0.29 0.32 2.13 4.22 Separated Axioms 3.63 Separated Axioms 0.04 0.05 72.33 UK UK Alt-Ergo � Alt-Ergo Alt-Ergo � Alt-Ergo MO Grouped Axioms MO MO MO UK 0.03 0.03 TO 1.44 4.50 1.44 6.58 TO UK 0.21 0.20 TO 125.77 54.06 25.34 85.26 TO 1.79 0.03 0.03 TO TO 3.72 0.03 0.03 TO 1.95 0.90 1.27 2.66 Grouped Axioms TO 2 nd call to Add_Next in Try_Row 107.33 108.67 0.03 0.03 TO loop-invariant in Try_Row 1.06 2.13 1.24 1.89 Alt-Ergo � Alt-Ergo Alt-Ergo � Alt-Ergo TO call to Copy_Until in Try_Row 0.02 0.02 0.03 0.03 0.41 1 st call to Add_Next in Try_Row 10.30 4.22 10.30 4.89 ring buffer: TO post of Add_Next 6.37 7.52 0.04 0.03 0.92 2 nd call to Add_Next in Try_Row 53.01 107.33 53.11 128.15 call to Append in Model 2.32 645.49 2.32 7.11 0.92 post of Copy_Until 0.37 UK 1.13 1.12 TO post of Add_Next 7.40 6.37 7.36 7.42 post of Model 848.04 TO 540.92 836.25 TO post of Try_Row 18.24 18.30 0.03 0.03 TO post of Copy_Until 0.94 0.37 0.89 1.47 post of Push 6.55 1299.29 8.66 1437.05 TO post of Try_Row 7.02 18.24 7.02 18.32 post of Pop 5.18 3.14 6.50 8.64 ring buffer: call to Append in Model 545.06 72.08 0.03 0.04 0.23 ring buffer: amortized queue: 0.56 post of Model TO MO 0.04 0.04 MO call to Append in Model 2.26 545.06 2.27 7.09 call to Insert in Tail 1.03 1.68 1.02 1.84 TO post of Push 762.44 364.29 0.03 0.05 TO post of Model 1090.18 TO 713.06 820.12 discr check in Tail 1.50 2.20 1.46 2.35 TO call to Is_Model in post of Push 0.04 0.11 0.03 0.03 0.69 post of Push 1047.97 762.44 TO 716.32 call to Insert in Enqueue 0.58 2.21 0.57 2.30 TO call to & in post of Push 0.06 202.72 0.03 0.03 0.50 post of Pop 342.23 6.18 23.56 16.40 discr check in Enqueue 1.58 2.21 1.58 1.12 0.75 post of Pop 6.18 4.66 0.23 0.48 TO amortized queue: post of Model 0.27 8.47 0.24 42.86 TO Range check in Is_Model 0.23 5.28 0.21 5.27 post of Tail 160.03 226.44 132.02 674.79 amortized queue: call to & in Model 0.11 0.35 0.03 0.03 0.37 call to Insert in Tail 1.04 1.71 1.02 1.84 call to & in post of Tail 0.34 0.97 0.33 0.89 TO call to Insert in Tail 1.71 3.71 0.04 0.04 0.60 discr check in Tail 1.50 2.55 2.14 2.37 post of Enqueue 95.64 217.53 110.67 1321.88 TO discr check in Tail 2.55 3.42 0.04 0.04 0.64 call to Append in Enqueue 0.20 0.11 0.03 0.03 0.49 call to Insert in Enqueue 2.20 4.29 0.03 0.03 0.55 discr check in Enqueue 1.05 4.72 0.03 0.03 0.57 post of Model 5.97 1664.58 0.04 0.10 TO post of Is_Model 0.06 0.06 0.03 0.03 TO post of Tail 865.03 TO 3.56 381.35 error error call to & in post of Enqueue 1.57 0.18 1.57 0.16 TO post of Enqueue 2801.83 2083.67 97.68 TO TO call to & in post of Tail 1.36 1.06 1.43 1.01 0.56 post of Tail 201.28 865.03 200.23 3033.20 TO post of Model 0.49 5.97 0.44 26.94 TO discr check in Enqueue 1.61 1.05 1.62 1.10 0.64 call to Insert in Enqueue 0.60 2.20 0.59 2.32 call to & in post of Enqueue 0.49 0.16 0.49 0.15
	call to & in post of Tail	1.06	4.77	0.04	0.04	0.24 336.56
	post of Enqueue	2083.67	TO	2.04 40.32	error	error
	call to & in post of Enqueue	0.18	0.88	0.03	0.03	MO	MO
	Figure 6.1: Time (in seconds) needed to solve all the tests with Alt-Ergo, Z3 3.2, and CVC4 1.2
	with a timeout of 3600s with and without the triggers. TO stands for timeout, UK for unknown
	and MO for out of memory.

  ∀e 1 e 2 e 3 : element_type.[equal_elements(e 1 , e 2 ), equal_elements(e 2 , e 3 )] equal_elements(e 1 , e 2 ) ≈ t → equal_elements(e 2 , e 3 ) ≈ t → equal_elements(e 1 , e 3 ) ≈ t ∀e 1 e 2 e 3 : element_type.[equal_elements(e 1 , e 2 ), equal_elements(e 1 , e 3 )] equal_elements(e 1 , e 2 ) ≈ t → equal_elements(e 2 , e 3 ) ≈ t → equal_elements(e 1 , e 3 ) ≈ t : list, cu 1 cu 2 : cursor.[position(co, cu 1 ), position(co, cu 2 )] position(co, cu 1 ) > 0 → position(co, cu 1 ) ≈ position(co, cu 2 ) → cu 1 ≈ cu 2 INSERT_POSITION_NO_ELEMENT: ∀co 1 , co 2 : list, cu : cursor, e : element_type.[insert(co 1 , no_element, e, co 2 ), position(co 1 , cu)] (insert(co 1 , no_element, e, co 2 ) ≈ t ∧ position(co 1 , cu) > 0) → position(co 1 , cu) ≈ position(co 2 , cu) ∀co 1 , co 2 : list, cu : cursor, e : element_type.[insert(co 1 , no_element, e, co 2 ), position(co 2 , cu)](insert(co 1 , no_element, e, co 2 ) ≈ t ∧ position(co 2 , cu 2 ) < length(co 2 )) → position(co 1 , cu) ≈ position(co 2 , cu) INSERT_ELEMENT: ∀co 1 , co 2 : list, cu 1 cu 2 : cursor, e : element_type.[insert(co 1 , cu 1 , e, co 2 ), element(co 1 , cu 2 )] (insert(co 1 , cu 1 , e, co 2 ) ≈ t ∧ position(co 1 , cu 2 ) > 0) → element(co 1 , cu 2 ) ≈ element(co 2 , cu 2 ) ∀co 1 , co 2 : list, cu 1 cu 2 : cursor, e : element_type.[insert(co 1 , cu 1 , e, co 2 ), element(co 2 , cu 2 )] (insert(co 1 , cu 1 , e, co 2 ) ≈ t ∧ position(co 1 , cu 2 ) > 0) → element(co 1 , cu 2 ) ≈ element(co 2 , cu 2 ) DELETE_RANGE: ∀co 1 , co 2 : list, cu : cursor.[delete(co 1 , cu, co 2 )] delete(co 1 , cu, co 2 ) ≈ t → position(co 1 , cu) > 0 ∀co 1 , co 2 : list, cu 1 cu 2 : cursor.[delete(co 1 , cu 1 , co 2 ), position(co 1 , cu 2 )] (delete(co 1 , cu 1 , co 2 ) ≈ t ∧ position(co 1 , cu 2 ) < position(co 1 , cu 1 )) → position(co 1 , cu 2 ) ≈ position(co 2 , cu 2 ) ∀co 1 , co 2 : list, cu 1 cu 2 : cursor.[delete(co 1 , cu 1 , co 2 ), position(co 2 , cu 2 )] (delete(co 1 , cu 1 , co 2 ) ≈ t ∧ 0 < position(co 2 , cu 2 ) < position(co 1 , cu 1 )) → position(co 1 , cu 2 ) ≈ position(co 2 , cu 2 ) DELETE_POSITION_AFTER: ∀co 1 , co 2 : list, cu 1 cu 2 : cursor.[delete(co 1 , cu 1 , co 2 ), position(co 1 , cu 2 )] (delete(co 1 , cu 1 , co 2 ) ≈ t ∧ position(co 1 , cu 2 ) > position(co 1 , cu 1 )) → position(co 1 , cu 2 ) ≈ position(co 2 , cu 2 ) + 1 ∀co 1 , co 2 : list, cu 1 cu 2 : cursor.[delete(co 1 , cu 1 , co 2 ), position(co 2 , cu 2 )] (delete(co 1 , cu 1 , co 2 ) ≈ t ∧ position(co 2 , cu 2 ) ≥ position(co 1 , cu 1 )) → position(co 1 , cu 2 ) ≈ position(co 2 , cu 2 ) + 1 DELETE_POSITION_NEXT: ∀co 1 ,co 2 : list, cu : cursor.[delete(co 1 , cu, co 2 )]delete(co 1 , cu, co 2 ) ≈ t → �next(co 1 , cu)�� DELETE_ELEMENT: ∀co 1 , co 2 : list, cu 1 cu 2 : cursor.[delete(co 1 , cu 1 , co 2 ), element(co 1 , cu 2 )] (delete(co 1 , cu, co 2 ) ≈ t ∧ position(co 2 , cu 2 ) > 0) → element(co 1 , cu 2 ) = element(co 2 , cu 2 ) ∀s 1 , s 2 : set α.[equal_sets(s 1 , s 2 )]equal_sets(s 1 , s 2 ) ≈ t → (∀x : α.[mem(x, s 1 )]mem(x, s 1 ) ≈ t → mem(x, s 2 ) ≈ t) ∧ (∀x : α.[mem(x, s 2 )]mem(x, s 2 ) ≈ t → mem(x, s 1 ) ≈ t) ∀s 1 , s 2 : set α, x : α.[mem(x, union(s 1 , s 2 ))] mem(x, union(s 1 , s 2 )) ≈ t → mem(x, s 1 ) ≈ t ∨ mem(x, s 2 ) ≈ t ∀s 1 , s 2 : set α, x : α.[union(s 1 , s 2 ), mem(x, s 1 )]mem(x, s 1 ) ≈ t → mem(x, union(s 1 , s 2 )) ≈ t ∀s 1 , s 2 : set α, x : α.[union(s 1 , s 2 ), mem(x, s 2 )]mem(x, s 2 ) ≈ t → mem(x, union(s 1 , s 2 )) ≈ t

	B Why3 Sets
	A.1 Axiomatization B.1 Axiomatization for Sets
	LENGTH_GTE_ZERO:
	∀co : list.[length(co)]length(co) ≥ 0
	IS_EMPTY:

∀co : list.[is_empty(co)]is_empty(co) ≈ t ↔ length(co) ≈ 0 EMPTY_IS_EMPTY: is_empty(empty) EQUAL_ELEMENTS_REFL: ∀e : element_type.[equal_elements(e, e)]equal_elements(e, e) ≈ t EQUAL_ELEMENTS_SYM: ∀e 1 e 2 : element_type.[equal_elements(e 1 , e 2 )] equal_elements(e 1 , e 2 ) ≈ equal_elements(e 2 , e 1 ) EQUAL_ELEMENTS_TRANS: POSITION_GTE_ZERO: ∀co : list, cu : cursor.[position(co, cu)] length(co) ≥ position(co, cu) ∧ position(co, cu) ≥ 0 POSITIONNO_ELEMENT: ∀co : list.[position(co, no_element)]position(co, no_element) ≈ 0 POSITION_EQ: ∀co DELETE_LENGTH: ∀co 1 , co 2 : list, cu : cursor.[delete(co 1 , cu, co 2 )] delete(co 1 , cu, co 2 ) ≈ t → length(co 2 ) + 1 ≈ length(co 1 ) DELETE_POSITION_BEFORE: EQ_DEF :

EQ_INV :

∀s 1 , s 2 : set α.[equal_sets(s 1 , s 2 )]equal_sets(s 1 , s 2 ) � ≈ t → (∃x : α.mem(x, s 1 ) ≈ t ∧ mem(x, s 2 ) � ≈ t ∨ mem(x, s 2 ) ≈ t ∧ mem(x, s 1 ) � ≈ t))

EXTENSIONALITY :

∀s 1 , s 2 : set α.[s 1 � ≈ s 2 ]equal_sets(s 1 , s 2 ) ≈ t → s 1 ≈

s 2 SUBSET_DEF : ∀s 1 , s 2 : set α.[subset(s 1 , s 2 ))] subset(s 1 , s 2 ) ≈ t → (∀x : α.[mem(x, s 1 )]mem(x, s 1 ) ≈ t → mem(x, s 2 ) ≈ t) SUBSET_INV : ∀s 1 , s 2 : set α.[subset(s 1 , s 2 ))] subset(s 1 , s 2 ) � ≈ t → (∃x : α.mem(x, s 1 ) ≈ t ∧ mem(x, s 2 ) � ≈ t) UNION_DEF : UNION_INV1 : UNION_INV2 : DELETE_ELEMENT_1:

These three hypothesis are not needed to apply T -Backjump. Still, to implement conflict analysis, we want to make j as small as possible.

http://bware.lri.fr/index.php/BWare _ project

subprograms that cannot be specified using only contracts. It is the case for example, for subprograms that require higher order reasoning, like a function that sums the elements of an array of integers, or programs that need to be linked to an existing WhyML library, like numeric elementary functions -square-root, logarithm... This mechanism can also be used to customize the translation of complex packages so that it allows the proof of user code to go through more easily, for example by providing triggers and splitting defining axioms. Finally, this mechanism can be used as a work-around for current SPARK 2014 limitations. It allows in particular to give defining axioms to regular functions and to quantify over non-integer values, for example the cursors of a container. For a mix of all these reasons, this mechanism is used in SPARK 2014 for formal containers, using manual translations that we have provided.

http://vscomp.org/

For a subprogram, the WhyML module contains a logic function -that can be called in assertions -a program function with contracts -that can be called in programs -and a procedure used to verify absence of runtime errors and conformance to contracts.

Consider the function G that calls the function F. We assume that F _ Pre and F _ Post are the precondition and postcondition of F and R _ Inv is an invariant over elements of type R that happens to be preserved by F: function G (X : R) return R is (F (X))

with Pre => R _ Inv (X) and then F _ Pre (X), Post => R _ Inv (X) and then F _ Post (G'Result, X);

Here is the translation of G into WhyML, a bit simplified for readability: Copy returns a vector whose elements and cursors are initialized from the corresponding elements and cursors of Source. If Capacity is 0, then the vector capacity is the length of Source; if Capacity is equal to or greater than the length of Source, the vector capacity is at least the specified value. Otherwise, the operation propagates Capacity _ Error.

The API also contains other helpful subprograms, in particular equality over vectors, functions for searching elements in a vector and subprograms for iterating over cursors: If the cursor Position equals No _ Element or designates the first element of Container, then

Previous returns the value No _ Element. Otherwise, it returns a cursor that designates the element with index To _ Index (Position) -1 in Container.

Translation into WhyML

Formal containers are translated through a special mechanism that we have implemented inside the SPARK 2014 tool. It allows to directly provide a manually written Why3 translation for potentially generic SPARK 2014 packages annotated with a dedicated pragma. This Why3 file must mimic the result of the translation on the Ada package so that SPARK 2014 can use its declarations. This mechanism can be useful for several reasons. First of all, there are some SPARK

A Decision Procedure for Formal Vectors

In this section, we define a theory that models some of the Ada formal vectors package's aspects. This theory is then axiomatized so that it can be used to verify SPARK 2014 programs using Formal Vectors.

Glossary Glossary

Super-literal either a literal, a closure, or an anti-closure. 59

Theory clause disjunctive set of closures. 24

Truth assignment set of further facts that a solver may assume from a set of theory clause using only propositional reasoning. 24

User clause a disjunctive set of literals. 59

RIGHT_NO_ELEMENT:

A.2. Tests in WhyML EQUAL_LISTS_POSITION:

A.

∀x, y : α, s : set α.[add(y, s), mem(x, s)]mem(x, s) ≈ t → mem(x, add(y, s)) ≈ t ADD_INV2 :

∀y : α, s : set α.[add(y, s)]mem(y, add(y, s)) ≈ t REMOVE_DEF : ∀x, y : α, s : set α.[mem(x, remove(y, s))]mem(x, remove(y, s))

∀co 1 , co 2 : vector, i, j : int, e : element_type.[replace_element(co 1 , i, e, co 2 ),to_cursor(co 1 , j)] replace_element(co 1 , i, e, co 2 ) ≈ t → index_type__zero < j ≤ length(co 1 ) + index_type__zero → to_cursor(co 1 , j) ≈ to_cursor(co 2 , j) ∀co 1 , co 2 : vector, i, j : int, e : element_type.[replace_element(co 1 , i, e, co 2 ),to_cursor(co 2 , j)] replace_element(co 1 , i, e, co 2 ) ≈ t → index_type__zero < j ≤ length(co 1 ) + index_type__zero → to_cursor(co 1 , j) ≈ to_cursor(co 2 , j)

C.1. Axiomatization for Formal Vectors

∀co 1 , co 2 , r : vector, i, j : int.[insert(co 1 , i, co 2 , r),to_cursor(co

C.1. Axiomatization for Formal Vectors SWAP_CAPACITY:

C.1. Axiomatization for Formal Vectors CONCAT_LENGTH:

CONCAT__2__DEF:

CONCAT__3__DEF:

C.1. Axiomatization for Formal Vectors CONCAT__4__DEF:

∀v : vector, e : element_type, n procedure Two _ Way _ Sort (A : in out Arr) with Post => (if Length (A) > 0 then (for some K in First _ Index (A) .. Last _ Index (A) => Element (A, K) = Element (A, K) and then (for all J in First _ Index (A) .. K -1 => not Element (A, J)) and then (for all J in K+1 .. Last _ Index (A) =>

C.2. Tests in SPARK 2014