Safe · Proved · Fast

Code proved correct,
compiled to native speed.

Byron is a compiler and prover for Ada and SPARK, the language NVIDIA chose for its security-critical firmware. Write it with braces like C or Rust, as typed Python, or as Ada itself. Byron proves there is no overflow, no out-of-bounds index and no broken contract, then compiles your program to native code.

Safe

Types that mean something

Strong static types, with ranges you declare: a percentage is 0 to 100 and the compiler holds you to it. No undefined behaviour, no null dereferences, no garbage collector.

Proved

Checked before it runs

Every contract and every run-time check is proved when you build, with no separate tool to remember. When a proof fails, you get the input that breaks it.

Fast

Native, with nothing left to check

Byron compiles through LLVM to native code. A check that is proved is removed from the binary, so the safety costs nothing at run time.

A first program

Pick the syntax
that reads like home.

The same small package, with a contract on each function, in each of Byron's three syntaxes. All three prove the same twelve checks, run the same way and print the same output.

package Counters {
    pub fn clamp(value, low, high: Integer) -> Integer
        requires low <= high
        ensures result in low..=high
    {
        if value < low {
            return low;
        } else if value > high {
            return high;
        }
        return value;
    }

    pub fn increment(count: inout Natural)
        requires count < Natural::Last
        ensures count == old(count) + 1
    {
        count += 1;
    }
}
from byron import Natural, ensures, requires, result


def clamp(value: int, low: int, high: int) -> int:
    requires(low <= high)
    ensures(low <= result <= high)
    if value < low:
        return low
    elif value > high:
        return high
    return value


def increment(count: Natural) -> Natural:
    requires(count < Natural.last)
    ensures(result == count + 1)
    return count + 1
package Counters is

   function Clamp (Value, Low, High : Integer) return Integer
     with Pre  => Low <= High,
          Post => Clamp'Result in Low .. High;

   procedure Increment (Count : in out Natural)
     with Pre  => Count < Natural'Last,
          Post => Count = Count'Old + 1;

end Counters;
package body Counters is

   function Clamp (Value, Low, High : Integer) return Integer is
   begin
      if Value < Low then
         return Low;
      elsif Value > High then
         return High;
      else
         return Value;
      end if;
   end Clamp;

   procedure Increment (Count : in out Natural) is
   begin
      Count := Count + 1;
   end Increment;

end Counters;
terminal
$ byronc prove
proved 12 of 12 checks in 3 subprograms (5 statically, 7 by the solver)

$ byronc run
hits: 2
clamped: 10

requires and ensures are the contract: what a caller must promise, and what the function promises back. byronc run proves and runs at once; byronc build links a native executable.

Bring what you know

Your instincts,
Ada's guarantees.

You don't have to learn Ada's syntax to get Ada's safety. Byron is one language with three spellings, and a project can mix all three: they share one prover, one compiler and one set of guarantees.

From C, C++, Rust or Go · .leon

Leon

Braces, fn, let and var, pub, and method chains like xs.filter(x => x > 0).sum(). A call marks what it may change, increment(&hits), and forgetting the & is an error with a fix-it.

From Python · .py

Leno

Typed Python, with lists, range, classes and generators. Contracts are ordinary calls, requires(...) and ensures(...), so a Leno program is still a Python program: CPython prints what the native build prints.

From Ada · .ads · .adb

Noel

Ada 2022 itself, with SPARK's contracts. Every legal Byron program is a legal Ada program with the same meaning, and Byron's test suite holds it to GNAT and GNATprove.

byronc translate

Move between them at any time.

byronc translate --to noel rewrites a Leon or Leno project as Ada, comments included, and back again. Start in the syntax your team knows, and hand the proved Ada to anyone who needs it.

Why Ada

The safe language
that was here first.

Ada was designed for software that must not fail, and SPARK adds proof on top. Both are having a moment, for good reasons.

Chosen by NVIDIA for security-critical firmware

NVIDIA moved security-critical firmware for its GPUs from C to SPARK, the provable form of Ada that Byron is built around. Its engineers reported generated code as fast as their C, and with every property proved, no run-time checks to pay for.

Sources: AdaCore case study , DevClass

Rust-grade safety, without fighting a borrow checker

Ada's parameter modes, in, out and in out, let the compiler decide how to pass a value, so most code never touches a pointer and there are no lifetimes to annotate. Where you do use pointers, ownership rules like Rust's apply, and only there.

Proof, not just memory safety

Memory safety rules out one class of bug. SPARK goes further: it proves your program has no overflow, no out-of-range value and no division by zero, and that it does what its contracts say.

Mature, standard and battle-tested

Ada is an ISO standard, now Ada 2022, with decades of use in avionics, rail and space. Every Byron program is legal Ada, so what you write is never locked in.

When a proof fails

The input that breaks it.

Take the requires line out of Leon's increment and the proof fails. Byron doesn't just say "might overflow": it finds the values that break the program, replays them to show the failure is real, and proposes the precondition that excludes exactly those values, already proved to be enough.

Every diagnostic is numbered, and byronc explain B4101 prints what it means and what to do. Diagnostics come as text or as JSON for editors and agents.

terminal
$ byronc prove
error[B4101]: overflow check fails
  --> ./src/counters.leon:17:15
   |
17 |         count += 1;
   |               ^^^^ might overflow `Integer` (-2147483648 .. 2147483647)
   |
   = note: replaying these values on the program reaches this check and it fails
   = counterexample: fails when count = Integer::Last
   = failing inputs: exactly count >= Integer::Last
   = help: a precondition `count <= 2147483646` on `Counters.increment` excludes exactly the values that fail, and is proved to make this check pass

1 error emitted
proved 9 of 10 checks in 3 subprograms (4 statically, 5 by the solver)

What is proved

Proof is a phase
of compilation.

A build succeeds only when flow analysis passes and every verification condition is discharged. There is no separate tool to remember to run, and no gap between the program that was proved and the program that ships.

Run-time checks

Every check Ada makes — overflow, range, index, division by zero, null, discriminants, tags, lengths — is proved never to fail. Proved checks are left out of the code, and LLVM is told the arithmetic they guard cannot wrap.

Contracts

Pre and Post, Contract_Cases, Exceptional_Cases, loop invariants and variants, type invariants, predicates and assertions. Ghost code and mathematical models exist for proof only and are erased.

Flow

Every variable is initialised before it is read, Global and Depends tell the truth, and ownership rules hold for access values, so there is no aliasing the prover cannot see.

Concurrency

Ada 2022's Jorvik profile: tasks and protected objects. Byron proves there are no data races, no blocking inside protected actions, and that priority ceilings are respected.

Exceptions

A procedure may propagate only the exceptions its Exceptional_Cases list, with their consequences proved. Any other raise must be proved unreachable, and checks never raise at all.

Devices

Volatile objects with SPARK's external properties, objects at fixed addresses, and overlays the prover can trust: the groundwork for drivers and embedded code.

See them at work in the examples: pipelines with generated loop invariants, Python lists with proved bounds, and tasks proved free of data races.

One command

Everything in
byronc.

The compiler, prover, JIT, REPL, formatter, translator and language server are one program. In the REPL, proof is advisory and run-time checks stay on, so a failed check is reported and you're back at the prompt.

byronc run

Prove, compile in memory and run, in one step.

byronc build

Prove, compile each unit to an object file and link a native executable.

byronc repl

Declarations, statements and expressions, with run-time checks on.

byronc translate

Rewrite a project in another syntax, comments included.

byronc explain

The full explanation of any numbered diagnostic, with an example and the fix.

byronc lsp

A language server: diagnostics, hover, go to definition, fix-its, formatting.

byronc stubs

Python stubs, so pyright and mypy read Leno code as they read any other.

--certificates

Keep each proof's unsat core in files you commit, re-checked anywhere without search.

byronc repl --syntax leon
$ byronc repl --syntax leon
byron> import Counters; use Counters;
byron> var hits = Natural::Last;
byron> clamp(42, 0, 10)
 10
byron> increment(&hits);
<input 4>:1:12: run-time check failed [B5101]: the precondition of `increment` might not hold
warning[B5101]: precondition fails at a call
  --> <input 4>:1:12
   |
 1 | increment(&hits);
   |            ^^^^ the precondition of `increment` might not hold
   |
  ::: ./src/counters.leon:15:18
   |
15 |         requires count < Natural::Last
   |                  --------------------- precondition
   |
   = note: running the program reaches this check and it fails
   = note: advisory mode: this is checked when the program runs

1 warning emitted

Status

Under active development.

Byron is young and moving fast, and everything it ships carries a permissive licence. If you build software that has to be right, we'd like to hear from you.