Examples

Real programs,
really proved.

Every program on this site is a project that the current byronic proves and runs, and every terminal shows what it printed. Nothing is retouched.

Leon and Noel

Pipelines with the invariants written for you

Reductions, filters, quantifiers and collections over arrays. In Leon they're method chains, A.filter(X => X >= Mark).sum(); in Noel they're Ada 2022's 'Reduce and iterated aggregates. Each pipeline becomes one fused loop whose invariants Byron generates, so overflow and index checks are proved without a single annotation. Where you write the loop yourself, as in Running_Total, it is proved against the reduction it computes.

//  Pipelines over arrays: reductions, filters, quantifiers and collections, each one
//  fused loop with generated invariants; reductions in contracts mean ghost folds.
package Stats {
    pub subtype Score = Integer range 0..=100;
    pub type Scores = [Positive..]Score;

    //  the sum, and the sum of those that pass
    pub fn Total(A: Scores) -> Integer
        requires A::Length <= 1000
    = A::Reduce("+", 0);

    pub fn Passing_Total(A: Scores, Mark: Score) -> Integer
        requires A::Length <= 1000
    = A.filter(X => X >= Mark).sum();

    //  how many pass: each counts one
    pub fn Passing(A: Scores, Mark: Score) -> Natural
        requires A::Length <= 1000
    = A.filter(X => X >= Mark).count();

    //  a range's values, mapped (the range's bounds bound its values)
    pub subtype Count = Natural range 0..=100;

    pub fn Sum_Of_Squares(N: Count) -> Natural = [for I in 1..=N => I * I]::Reduce("+", 0);

    //  the largest, by a function reducer
    pub fn Best(A: Scores) -> Score = A::Reduce(Score::Max, 0);

    //  quantifiers, with and without a filter
    pub fn All_Valid(A: Scores) -> Boolean = A.all(X => X <= 100);

    pub fn Any_Perfect(A: Scores) -> Boolean = A.any(X => X == 100);

    //  the scores that pass, collected into an array as long as their number
    pub fn Above(A: Scores, Mark: Score) -> Scores
        requires A::Length <= 1000
    = A.filter(X => X > Mark).collect();

    //  each score curved, collected; nothing filtered, so as long as the source
    pub fn Curved(A: Scores) -> Scores
        requires (A::First == 1) & (A::Length <= 1000)
        ensures result::Length == A::Length
    = A.map(X => (X + 100) / 2).collect();

    //  a loop the programmer writes, proved against the reduction it computes
    pub fn Running_Total(A: Scores) -> Integer
        requires A::Length <= 1000
        ensures result == A::Reduce("+", 0)
    {
        var S: Integer = 0;
        for I in A::Range {
            invariant (S == [for J in A::First..=I - 1 => A[J]]::Reduce("+", 0)) & (S <= 100 * (I - A::First));
            S = S + A[I];
        }
        return S;
    }
}
import Ada.Text_IO;
use Ada.Text_IO;
import Stats;
use Stats;
fn Main() {
    let Class: Scores = (72, 45, 100, 88, 61, 39, 95);
    let High: Scores = Above(Class, 80);
    let Fair: Scores = Curved(Class);
    Put_Line("total" ++ Integer::Image(Total(Class)) ++ Integer::Image(Running_Total(Class)));
    Put_Line("passing" ++ Integer::Image(Passing(Class, 60)) ++ Integer::Image(Passing_Total(Class, 60)));
    Put_Line("squares" ++ Integer::Image(Sum_Of_Squares(10)));
    Put_Line("best" ++ Integer::Image(Best(Class)));
    Put_Line("valid " ++ Boolean::Image(All_Valid(Class)) ++ ", perfect " ++ Boolean::Image(Any_Perfect(Class)));
    Put("above 80:");
    for S in High {
        Put(Integer::Image(S));
    }
    New_Line();
    Put("curved:");
    for S in Fair {
        Put(Integer::Image(S));
    }
    New_Line();
}
--  Pipelines over arrays: reductions, filters, quantifiers and collections, each one
--  fused loop with generated invariants; reductions in contracts mean ghost folds.
package Stats is
   subtype Score is Integer range 0 .. 100;
   type Scores is array (Positive range <>) of Score;

   --  the sum, and the sum of those that pass
   function Total (A : Scores) return Integer is (A'Reduce ("+", 0))
   with Pre => A'Length <= 1000;

   function Passing_Total (A : Scores; Mark : Score) return Integer is
     ([for X of A when X >= Mark => X]'Reduce ("+", 0))
   with Pre => A'Length <= 1000;

   --  how many pass: each counts one
   function Passing (A : Scores; Mark : Score) return Natural is
     ([for X of A when X >= Mark => 1]'Reduce ("+", 0))
   with Pre => A'Length <= 1000;

   --  a range's values, mapped (the range's bounds bound its values)
   subtype Count is Natural range 0 .. 100;
   function Sum_Of_Squares (N : Count) return Natural is
     ([for I in 1 .. N => I * I]'Reduce ("+", 0));

   --  the largest, by a function reducer
   function Best (A : Scores) return Score is (A'Reduce (Score'Max, 0));

   --  quantifiers, with and without a filter
   function All_Valid (A : Scores) return Boolean is (for all X of A => X <= 100);
   function Any_Perfect (A : Scores) return Boolean is (for some X of A => X = 100);

   --  the scores that pass, collected into an array as long as their number
   function Above (A : Scores; Mark : Score) return Scores is
     ([for X of A when X > Mark => X])
   with Pre => A'Length <= 1000;

   --  each score curved, collected; nothing filtered, so as long as the source
   function Curved (A : Scores) return Scores is ([for X of A => (X + 100) / 2])
   with Pre => A'First = 1 and A'Length <= 1000,
        Post => Curved'Result'Length = A'Length;

   --  a loop the programmer writes, proved against the reduction it computes
   function Running_Total (A : Scores) return Integer
   with Pre => A'Length <= 1000,
        Post => Running_Total'Result = A'Reduce ("+", 0);
end Stats;
package body Stats is
   function Running_Total (A : Scores) return Integer is
      S : Integer := 0;
   begin
      for I in A'Range loop
         pragma Loop_Invariant
           (S = [for J in A'First .. I - 1 => A (J)]'Reduce ("+", 0)
            and S <= 100 * (I - A'First));
         S := S + A (I);
      end loop;
      return S;
   end Running_Total;
end Stats;
terminal
$ byronic prove
proved 64 of 64 checks in 11 subprograms (6 statically, 58 by the solver)

$ byronic run
total 500 500
passing 5 416
squares 385
best 100
valid TRUE, perfect TRUE
above 80: 100 88 95
curved: 86 72 100 94 80 69 97

Leno

Python that is proved, then compiled

Lists, range, generators and all(...), with contracts and loop invariants as ordinary calls. Byron proves that no index is out of bounds and no sum overflows, then compiles to native code. Run the same files with python3, with Byron's byron module on the path, and they print the same lines.

from byron import ensures, invariant, requires, result


# the sum of at most 1000 scores of at most 100 points each
def total(xs: list[int]) -> int:
    requires(len(xs) <= 1000 and all(0 <= xs[k] <= 100 for k in range(len(xs))))
    ensures(0 <= result <= 100 * len(xs))
    sum_so_far = 0
    for i in range(len(xs)):
        invariant(0 <= sum_so_far <= 100 * i)
        sum_so_far += xs[i]
    return sum_so_far


# the position of the best score (the first, if several are equal)
def best(xs: list[int]) -> int:
    requires(len(xs) > 0)
    ensures(0 <= result < len(xs))
    position = 0
    for i in range(1, len(xs)):
        invariant(0 <= position < i)
        if xs[i] > xs[position]:
            position = i
    return position
from scores import best, total


def main() -> None:
    xs: list[int] = [58, 72, 69, 91, 80]
    print("total", total(xs))
    print("best at", best(xs))


if __name__ == "__main__":
    main()
terminal
$ byronic prove
proved 44 of 44 checks in 3 subprograms (8 statically, 36 by the solver)

$ byronic run
total 370
best at 3

$ python3 src/main.py
total 370
best at 3

Concurrency

Tasks proved free of data races

A producer and a consumer share a bounded buffer, a protected object whose entries wait on their barriers. Each task body and protected operation is proved like sequential code, and Byron checks the rest of what makes a Jorvik program correct: no data races, no blocking in protected actions, and priority ceilings respected.

"""A bounded buffer between tasks."""

from byron import Array, Modular, Natural, Protected, entry, when


class Slot(Modular, modulus=4):
    pass


class Ring(Array[int], index=Slot):
    pass


class Buffer(Protected):
    items: Ring = Ring([0, 0, 0, 0])
    first: Slot = 0
    count: Natural = 0

    @entry
    def put(self, x: int) -> None:
        when(self.count < 4)
        self.items[self.first + Slot(self.count)] = x
        self.count = self.count + 1

    @entry
    def get(self) -> int:
        when(self.count > 0)
        x = self.items[self.first]
        self.first = self.first + 1
        self.count = self.count - 1
        return x


shared = Buffer()
"""Two tasks: a producer of squares and a consumer that prints them, then ends the program."""

from byron import sleep, stop, task

from buffers import shared


@task
def producer() -> None:
    for i in range(1, 6):
        shared.put(i * i)
    while True:
        sleep(1.0)


@task
def consumer() -> None:
    for _ in range(5):
        x: int = shared.get()
        print("got", x)
    stop()
    while True:
        sleep(1.0)
import workers


# the program is its tasks: the main function has nothing to do
def main() -> None:
    pass
terminal
$ byronic prove
proved 11 of 11 checks in 4 subprograms (8 statically, 3 by the solver)

$ byronic run
got 1
got 4
got 9
got 16
got 25

Let the producer print too

Add print("put", i * i) to the producer and both tasks now write to standard output, which Byron treats as a shared variable. The program is rejected before it can interleave its output, with both uses shown and the remedy named.

terminal
$ byronic prove
error[B3019]: `Device` is shared by task `producer` and task `consumer` outside a protected object
  --> ./src/workers.py:12:9
   |
12 |         print("put", i * i)
   |         ^^^^^^^^^^^^^^^^^^^ used by task `producer`
...
21 |         print("got", x)
   |         --------------- used by task `consumer`
   |
  ::: <predefined>/ada-text_io.adb:6:4
   |
 6 |    Device : Boolean := False;
   |    ------ declared here
   |
   = note: tasks run at the same time: a variable more than one of them uses, one writing it, belongs in a protected object, whose actions hold its lock
   = help: make `Device` a component of a protected object, and use it through that object's operations

1 error emitted