Gospel Documentation
website·Indexed 29 days ago
https://ocaml-gospel.github.io/gospel/A language for writing formal specifications for OCaml programs. Gospel allows developers to annotate OCaml types and functions with contracts, models, and invariants, translating these specifications into separation logic. The documentation covers installation, language semantics, function contracts, and a standard library (Gospelstdlib) providing predicates for common types like lists, sets, and arrays.
What's inside Gospel
- Gospel provides a standard library containing types, functions, and predicates specifically designed for use in specifications. While it mimics the naming conventions and patterns of the OCaml standard library to ease onboarding, it consists exclusively of logical symbols. Users must use the Gospel standard library in specifications instead of the OCaml standard library, as the latter is not permitted in specifications.
Check for big-endian architecture in Gospelstdlib.Sys
Thebig_endianfunction returns a boolean indicating whether the current system architecture is big-endian.function big_endian : boolUse OCaml attributes for Gospel specifications
Gospel embeds specifications into OCaml files using OCaml attributes with the identifiergospel. The specifications are provided as strings within the attribute payload. Gospel uses two forms:[@@gospel "<spec>"]and[@@@gospel "<spec>"].Model a mutable data structure with a logical view
In Gospel, you can attach a logical model to a type declaration to reason about its state. Use themutable modelsyntax to define a field (likeview) that represents the mathematical state of the object. This allows specifications to refer to the logical state of a type that may be opaque in the OCaml implementation.type 'a t (*@ mutable model view: 'a sequence *)Handle preconditions and exceptions in Gospel
Gospel provides three primary ways to handle assumptions about function arguments:
- Exceptional: Use
raisesto specify the state of the model when a specific exception is thrown. - Unsafe (Precondition): Use
requiresto place the burden of proof on the caller. If the condition is not met, the behavior is undefined. - Defensive (Runtime Check): Use
checksto indicate the function internally verifies the condition and raisesInvalid_argumentif it fails.
(* Exceptional Version *) val pop: 'a t -> 'a (*@ v = pop q modifies q ensures old q.view = q.view ++ Sequence.cons v Sequence.empty raises Empty -> q.view = old q.view = Sequence.empty *) (* Unsafe Version *) val unsafe_pop: 'a t -> 'a (*@ v = unsafe_pop q requires q.view <> Sequence.empty modifies q ensures old q.view = q.view ++ (Sequence.cons v Sequence.empty) *) (* Defensive Version *) val pop: 'a t -> 'a (*@ v = pop q checks q.view <> Sequence.empty modifies q ensures old q.view = q.view ++ (Sequence.cons v Sequence.empty) *)- Exceptional: Use
Understand Gospel language semantics and tool capabilities
The semantics of the Gospel language are defined by its translation to separation logic. Thegospelcommand-line tool performs type-checking on Gospel-specified modules; however, to fully exploit the semantics of these specifications, users must employ additional specialized tools from the Gospel ecosystem.Verify array properties with Gospel predicates
Predicates used to assert properties about array contents, such as membership, quantification, and permutations.(* True if all elements of a satisfy f * predicate for_all (f: 'a -> bool) (a: 'a t) (* True if at least one element of a satisfies f * predicate _exists (f: 'a -> bool) (a: 'a t) (* True if x is equal to an element of a * predicate mem (x: 'a) (a: 'a t) (* True if a and b contain the same elements with the same occurrences * predicate permut (a b: 'a array) (* True if segment [lo..hi-1] of a is a permutation of [lo..hi-1] of b, and others are equal * predicate permut_sub (a b: 'a array) (lo hi: integer) (* Two-argument versions of for_all and _exists * predicate for_all2 (f: 'a -> 'b -> bool) (a: 'a t) (b: 'b t) predicate _exists2 (f: 'a -> 'b -> bool) (a: 'a t) (b: 'b t)Understand Gospel lexical conventions and OCaml compatibility
Gospel adopts most of OCaml's lexical conventions, including whitespace rules, variable naming conventions, and integer representation. Developers familiar with OCaml can apply the same lexical rules to Gospel, with a few specific additions regarding keywords, symbols, and type modifiers.Define logical axioms in Gospel
Axioms allow you to declare properties that are assumed to be true without providing a definition body.(*@ axiom unbounded_integers : forall i. exists j. j > i *)Specify preconditions using `requires` and `checks`
Preconditions define properties that must hold at function entry. Gospel provides two ways to express them:
requires: Defines conditions for well-specified behavior. If violated, the call is considered faulty, and all other contract guarantees (postconditions, etc.) are void.checks: Defines conditions that, if violated, cause the function to raise an OCamlInvalid_argumentexception without modifying state. The call is not considered faulty; the caller must handle the exception.
Interaction: If both are used,
requirestakes precedence.requires Pandchecks Qis equivalent torequires Pandchecks P -> Q.val eucl_division: int -> int -> int * int (*@ q, r = eucl_division x y requires y > 0 checks y != 0 ensures x = q * y + r *)Declare non-terminating functions with `diverges`
By default, OCaml functions with Gospel contracts are assumed to terminate. To specify that a function (such as a server loop or event listener) may not terminate, add thedivergeskeyword to the contract.val run : unit -> unit (*@ run () diverges (* ... *) *)Specify code equivalence in Gospel function contracts
Use theequivalentkeyword followed by a string containing OCaml code to specify that a function's behavior is equivalent to another piece of code. This is useful for functions whose behavior is difficult to express in pure logic. Note that the Gospel type-checker does not currently type-check the code insideequivalentclauses.type 'a t val iter : ('a -> unit) -> 'a t -> unit (*@ iter f t ... equivalent "List.iter f (to_list t)" *)