Why3find provides a collection of utilities to ease the development
of Why3 projects and the distribution of Why3 libraries and
packages. It can be used in place of the traditional why3
command for most
development tasks, eg. configuring provers, proving files or interactively
debugging proofs. In additional, it can be used to build enhanced documentation,
package and install libraries or setup a distributed solver cloud.
- Quick Setup
- Why3 Packages
- Package Development
- Package Configuration
- Package Proving
- Package Documentation
- Package Soundness
- OCaml Code Extraction
Let say you have some Why3 source files. Go into their directory and simply use the following commands:
$ why3find config --detect
$ why3find prove
This will configure Why3 with the SMT solvers installed on your computer,
and prove all your Why3 files in the directory. If any some proof failed,
you may try why3find prove -x
to dump a colored proof context
in your terminal, or why3find prove -i
to open Why3 IDE to further
debug your proofs. Re-run why3find prove
until proofs are complete.
At any time, you can generate a complete HTML documentation enhanced with proof witnesses and axiom dependencies with the following command:
$ why3find doc
To go further, continue reading this manual and use the following commands:
$ why3find [-h|--help] # Help on commands
$ why3find COMMAND [-h|--help] # Help on specific command
Disclaimer: Why3find is currently in early design stage and is subject to change in future versions of the system. We are also looking for feedback and are very opened to discussions and propositions. Please, do not hesitate to open issues on our public BTS for submitting bugs or exchanging on feature wishes and new ideas.
Why3find packages provides a way to share Why3 modules and theories across different projects. Hence, a package is essentially a collection of WhyML files, together with configuration files and metadata, including package dependencies, prover configuration, proof strategies, additional configuration, etc.
Typically, why3find
uses package metadata to properly configure Why3 for using
the configured packages when proving and generating documentation. To ease the
management of Why3 packages, why3find
provides the following commands:
$ why3find where # location of global repository
$ why3find shared # location of why3find shared resources
$ why3find list # list all installed packages
$ why3find query PKG # details on package PKG
Packages are installed or uninstalled thanks to the following commands:
$ why3find install PKG
$ why3find uninstall PKG...
By default, installation actually only generates a dune
file at the root
directory of the package with all the necessary installation instructions.
However, it is also possible to install files directly to the why3find
repository, by using why3find install --global PKG
, without using any dune
intermediate file.
A typical Why3 package project consists of user-edited files
and generated files from why3find
.
A new package PKG (usually a lowercase identifier) is typically initiated by the following command:
$ why3find init PKG [DIR]
This will create a directory DIR
(or PKG
by default) with a default
dune project file and git-ignore configuration. The directory DIR/PKG
is also
created. The package directory shall be structured as follows:
DIR
├── .why3find # Why3find local cache (*not* versioned)
├── why3find.json # Why3find configuration (versioned)
├── dune-project # Dune project configuration (versioned)
├── lib/** # OCaml extracted files (versioned)
└── PKG # Package source directory
├── **/*.mlw # Why3 source files (versioned)
├── **/*/proof.json # Why3find proof witness (versioned)
├── **/*/why3session.xml # Why3 internal files (*not* versioned)
└── **/*/why3shapes.gz # Why3 internal files (*not* versioned)
You shall put your Why3 source files in this sub-directory in order
to be compliant with Why3 global naming policy. Consider for instance
a package foo
with a source file foo/bar.mlw
declaring a module Jazz
,
this module will be named foo.bar.Jazz
in every place, either from
the package foo
or from another package depending on foo
.
Strictly speaking, if your package is not intended to be used as a package
dependency of another Why3 project, you might omit the PKG
sub-directory and
put your source files directly at the root directory. However, the why3find install
command will not work as expected and you will have to manage
installation by your own means. Other why3find
commands will work as expected,
typically for proving, extracted OCaml code and generating documentation.
Most why3find
commands support common options, for which default values can be
configured for the entire package. This is achieved by why3find config
command. The generated file why3find.json
is used by why3find
to locate the
root directory and to store its default configuration options.
By default, why3find
commands will look for a why3find.json
file into all
the parents of the current working directory. If not found, the current working
directory will be taken as the package root. Otherwise, you can specify by hand
the package root with option --root DIR
.
The common options are given below:
--root DIR # Specifies the package root directory
--extra-config CFG # Extra why3 configuration file
-p|--package PKG # Package to depend on
-P|--prover PRV # Provers to be used
-T|--tactic TAC # Tactics to be used
-D|--driver DRV # Extraction driver to be used for OCaml
Package, prover, tactic and driver options can be used to specify multiple items
at a time. Finer management is possible using modifier flags. Flag +
adds
items, flag -
remove items, flag =
replace items, flag n:
move or insert
items starting at position n
. A flag applies to all elements in the list,
until a new flag is emitted. Moreover, option none
can be used to remove all
items from the configuration. For instance:
--prover none # Remove all provers
--prover a,b,c # Use provers a, b and c only
--prover +a,b # Add provers a and b to package config
--prover -a,b,+c # Remove provers a and b from package config, then add c
--prover 2:a,b # Move or insert provers a and b at position 2 and 3
--prover +a,-b,1:c # Add prover a, remove b, move or insert c at position 1
Provers can be pinned to precise versions, using prover@version
. Prover names
are case-insensitive and may also refer to prover shortcuts from Why3
configuration.
The command why3find config
can be used to manage the package configuration.
Typical examples are:
why3find config # Show current config
why3find config […] # Update packages, provers, etc.
why3find config --reset […] # Configure from scratch (ignore current config)
why3find config --default […] # Use all "default" provers available from why3 configuration
why3find config --detect […] # Run `why3 config detect` before using all "default" provers
why3find config --check # Check consistency between proof certificates and configuration
When using --default
or --detect
why3find will select the highest available
versions of provers alt-ergo
, z3
, cvc4
and cvc5
from why3 configuration.
--detect
will first update why3 configuration by running why3 config detect
.
Why3find is known to work well with those provers.
The why3find prove
command is used to prove why3 lemmas and other proof
obligations. The command will try to discharge each proof obligation with one of
the configured provers. Alternatively, tactics can be used to simplify a goal
and recursively prove all the generated sub-goals. Tactics are Why3
transformations with no arguments. They shall transform tasks into equivalent
sub-tasks, since once a tactic has been applied, why3find prove will stick on it
(no back-tracking). Here are some basic examples of use:
$ why3find prove ;; prove all **/*.mlw files
$ why3find prove [FILE.mlw] ;; prove listed files only
$ why3find prove -x ;; print proof context on fail
$ why3find prove -i ;; run Why3 IDE on fail
$ why3find prove -m ;; minimize proofs (after debugging)
More details are provided below.
Proof Context can be printed for unproved goals can be printed
on the terminal by why3find prove -x
. Unproved goals are highlighted in
red, hypotheses and positive conditions in green and negative conditions
in magenta. Hypotheses and context are kept in a range of 5 lines around goals.
Use --context n
instead of -x
to output a context of n
lines instead.
Interactive Proving is activated by why3find prove -i
and will launch the
standard Why3 IDE in case of unproved goals. Notice that Why3 sessions will be
automatically generated in this case. However, proof certificates will not be
updated after exiting Why3 IDE: this interactive mode is meant for debugging
purpose, and you will have to re-launch why3find prove
to update the proof
certificates when your Why3 specifications have been fixed.
Proving Strategy for building proof certificates is a heuristic based on user-defined reference time, registered provers and tactics. It consists in several rounds tried in sequence until proof completion:
-
Fast sequential provers: each configured prover is tried in sequence with a short timeout (1/5 of the reference time).
-
Parallel provers: all provers are tried in parallel with the reference time as timeout.
-
Tactics: each configured or selected tactic is tried in sequence ; the first one that is applicable terminates the strategy for this goal and the generated sub-goals are scheduled for proof completion (there is no backtracking).
-
Final parallel long try: if no tactic applies, all provers are finally tried in parallel with a larger timeout (2 times the reference time).
In case the final round fails to complete the proof, the goal is marked stuck and all its parent goals are marked incomplete. Remark: incomplete proof certificates are also stored in order to be used as hints for further proof lookup. However, a tactic-node with all its sub-goals marked « stuck » would be removed.
The reference time is set to one second by default and can be modified with -t TIME
or configured using why3find config -t TIME
. A fraction of seconds or
suffix time units (h
,min
,s
,ms
) can be given, eg. 0.5
, 200ms
, 3min
.
The reference time is specified relatively to the master machine, Cf. prover
calibration below.
Proof search is pruned after a maximal number of nested levels. The default
depth is 6 and it can be modified with -d DEPTH
or configured using why3find config -d DEPTH
.
When using why3find prove -i
mode, the Why3 IDE is loaded with custom proof
strategies that mimics the why3find proof strategy:
- strategy
hammer
(keyboard shortcutH
) repeatedly applies the provers, then try the tactics, then finally retries the provers with a longer time. - strategy
provers
(keyboard shortcutP
) only applies the provers; - strategy
unfold
(keyboard shortcutU
) applies the tactics, then go into strategyhammer
.
Prover Cache is used to speed proof process. The hidden directory .why3find
at the root of the package directory stores a hash of each proof tasks send to
provers. You can bypass access to the cache with option --no-cache
. However,
it will still be updated for further usage. You may also safely remove the cache
by entirely suppressing the .why3find
directory.
Parallel Proving is used to run different provers parallel on the different
cores of your machine. You can specify the number of parallel provers with
option -j N
. Alternatively, you can also use why3find config -j N
to
configure it locally, in which case your personal Why3 configuration
~/.why3.conf
is updated accordingly. Parallel proving is also performed among
the different goals and proof obligations to be proved. However, priority is put
on goals that were incomplete with respect to the previously known proof
certificates. This strategy makes detection of stuck goals faster when debugging
proofs. During execution in a terminal, why3find prove
displays a progression
status with the following format:
$ why3find prove […]
P/Q/S/R goal goal …
P
is the number of complete proofs to be replayed; Q
is the number of stuck
or partial proofs to be complete; finally S
and R
are respectively the
number of schedules and currently running prover tasks. The topmost active goals
for proof completion are also given for user feedback.
Proof Certificates are stored by why3find prove
after each proof tasks. They
fulfill different purposes: they are used for proofs replay, as a hint database
for proofs update, and can also be used to report how some proof obligation
has been finally proved. Hence, proof certificates are very much like Why3
sessions, but with much less information details: they contain only prover
results with time information, tactics, and contain at most one proving strategy
for each goal. Proof certificates are also used when generating documentation.
Proof certificates are stored in proof.json
files located just after the usual
Why3 sessions files. The management of proof certificates is controlled by the
following options:
why3find prove -f # Force proof reconstruction from scratch (no hints)
why3find prove -u # Update proofs by using current certificate as hints (the default)
why3find prove -r # Replay proofs (no update, no proof completion)
why3find prove -m # Complete failed proofs and minimize proof trees
Prover Calibration is a strategy used by why3find
in order to reduce the
instability in proof replays introduced when using different machines. Such
differences are frequently observed between different packages or among several
developers in the same package. For a given proof certificate file, proof times
and timeouts are always given relatively to some reference computer named the
master machine. The velocity of a prover on a local machine with respect to
the master machine is evaluated by running a reference problem for some
parameter n
on both machines. This velocity is then used to convert local
times into reference times wrt to the master machine or vice et versa. The
why3find
commands related to prover calibration are :
$ why3find config -m # Calibrate provers on the master machine
$ why3find config -v # Evaluate velocity of provers on a local machine
It is highly recommended to update all proofs on the master machine with
why3find prove -f
after modifying the proof calibration. Usually, you
calibrate provers once at the very beginning of the project.
Prover calibration is known to work well with provers at-ergo
, z3
, cvc4
and cvc5
. It might also work out-of-the-box for other provers, provided they
support quantified anonymous function theories from the SMTLIB. Please report us
any calibration problem with your favorite provers, if any.
Proof Server can be used in order to increase parallelism and stability in
collaborative development. First, you need to launch a centralized why3find server
that will serve as a centralized proxy and will manage the proof
database on disk. Then, a number of why3find client
can dynamically connect to
the server to offer their locally available provers for proving tasks. Any user
can then connect to the server to obtain proof results, by using why3find prove --server
options. Hence, proof results can be shared among different users
during development. By launching a large number of clients, you can build large
proof clusters. Prover calibration of each contributing client is used by the
server to ensure the consistency of the distributed proof results.
The why3find doc
command is used to build HTML documentation for the package.
It can be used for the entire package or only a selection of source files or
directories. Unless specified, generated files go to the html
sub-directory
of the root package directory:
$ why3find doc PATH...
Each PATH
can be either a single Why3 *.mlw
file, a single markdown *.md
file or a directory to be recursively processed. Markdown files are treated
just like a plain (**…*)
comment inside Why3 source files.
Generated documentation output is a flat directory organized as follows:
html/fonts # Icon fonts
html/style.css # CSS resources
html/script.js # JavaScript resources
html/page.html # Documentation for markdown file
html/pkg.file.index.html # Documentation for Why3 source file
html/pkg.file.proof.html # Proof documentation for source file
html/pkg.file.Module.html # Documentation of module or theory
The generated files for Icons, CSS and JavaScript are imported from the
why3find share
directory. They can be replaced by your own files for further
customization.
Documentation Structure follows the order of documentation blocs, which shall
be put inside (** … *)
comments. Module and Theory documentation are placed
into separated files. The documentation bloc immediately before the module or
theory declaration (without empty line between) is put as an introduction in the
module or theory documentation file. For instance:
(** This goes into the index page *)
(** This is the header of module M's page *)
module M
…
end
(** This goes back to the index page *)
Remark: regular comments inside modules are printed as they are, along with the code. However, code comments that live outside modules are not printed since index pages only contains documentation blocs.
Basic Markdown Format can be used in plain markdown page and to why3 documentation comments. The following documentation snippets show examples:
# Header (level 1)
## Header (level 2, etc.)
Text with *emphasis* or **bold**.
Can also use _emphasis_ or __bold__.
Source `code`.
Itemized lists:
- item A
- item B
References:
- {qid} unambiguous reference (including modules)
- {t:qid} types
- {l:qid} functions and predicates
- {p:qid} properties (lemmas or axiom)
- {v:qid} program values
Notice that Why3 references works across packages, assuming documentation of external packages have been properly installed.
Code Sections can be delimited with fold/unfold. This is especially useful to hide some information details, typically proof details. Examples:
lemma Thm: forall x,y. …
(*proof*) by x = y -> … (*qed*)
Section delimiters can have various format:
(*proof*) … (*qed*) # folded by default
(*[Title]*) # open a section with « Title » (initially visible)
(*[Title]-*) # open an initially folded section (not visible)
(*[Title]+*) # open an initially unfolded section (visible)
(*/[End]*) # close current section with « End » closing title
Actually, (*proof*)…(*qed*)
is a shortcut for (*[Proof]-*)…(*/[Qed]*)
.
Nested sections are allowed. Notice also that sections are authorized to overlap
code's nested parentheses, although it is not recommended to do so for readability
reason. For instance (*proof*) begin (*qed*) end
is valid,
but makes the generated documentation difficult to read.
Cloned Modules and Theories sections are automatically inserted for clones, with a copy of the cloned symbols signature, and links to their original definitions.
Proof Results based on proof certificates generated by why3find prove
are also inserted after each goal identifier. Proof summaries are also inserted
for modules, theories and clones.
Consolidated Hypotheses for each module is also generated and added to the proof documentation. It consists of all the parameters (abstract types or logic symbols), abstract values and hypotheses (axioms) the module actually depends on. Axiom summaries are also inserted for modules, theories and clones.
Extracted and Built-in Symbols from prover drivers and extraction drivers are also taken into account when consolidating hypotheses. Extracted symbols are reported but not counted as parameters. Erased axioms or builtin symbols from prover drivers are considered to be built-in symbols and are not counted as actual hypotheses or parameters.
When proving a module or when generating the documentation of the package,
why3find
will also collect all the abstract definitions and properties through
all your dependencies. Indeed, the validity of your proofs generalizes to any
ground instance of abstract definitions that satisfies all the assumed
properties. Although, if no such ground instance exists, your proofs are still
valid from a logical point of view, but they have no useful value. In this respect,
a module with no ground instance can be considered to be unsound.
The why3find
documentation generator promotes the development of sound
packages by checking that every module with abstract parameters or assumed
properties is actually refined by a clone instance from some other sound
module. In the generated documentation, each module is annotated with its
abstract parameters and assumed properties; known instances are also reported
and soundness is checked by transitive closure. Hence, each abstract module is
expected to be cloned inside a sound module in order to witness the consistency
of its hypotheses.
Modules hypotheses are classified as follows:
-
Abstract types, constants, pure logic functions and predicates are sound since Why3 checks for all types to be inhabited (hence, including arrow types).
-
Axioms and declarations of abstract values with non-trivial post-conditions are all considered to be possibly unsound unless a sound clone instance of the module is witnessed.
-
Introduction of
any ...
values insidelet
definitions are considered to be always sound since Why3 generates proof obligations to demonstrate that such a value exists. -
Any assumed assertion (
assume { _ }
) inside alet
definition is considered to be always unsound since it is not possible to refine such an assumption by cloning. -
Any abstract values inside a
let
definition is also considered to be always unsound for the same reason. -
Hypotheses and abstract parameters from the Why3 standard library are all considered to be sound.
When proving a module with why3find prove
you can list all the abstract
parameters and assumed properties involved by your proofs with option -a
.
Other options are available, see why3find prove -h
for further details.
The why3find extract
command can be used to extract OCaml code from the
current Why3 package. More precisely, the command calls why3 extract
with
options corresponding to the project configuration and from the why3find extract
command line. Moreover, the why3find extract
command also generates a
dune
file for compiling and installing the library of extracted OCaml code.
You shall specify the the list of Why3 modules (MODULE
) to be extracted on the
command line, as follows:
why3find extract MODULE...
All modules shall belong to the same package. Options can be passed to
why3find extract
command:
why3find extract -d PKG # additional OCaml package dependency
why3find extract -o DIR # extraction output directory (default is "lib")
The extracted modules are not intended to be directly used from OCaml client
code. As a rule of thumb you shall write some safe OCaml layer upon the
extracted one, as a separated dune
library.
« Packaging proofs with Why3find » L. Correnson, CEA-LIST, JFLA 2024
« Why3 — Where Programs Meet Provers » J-C. Filliâtre, A. Paskevich, INRIA, ESOP 2021