Concrete is a systems programming language without garbage collection that checks resource ownership, makes external authority explicit in function signatures, and supports machine-checked proofs for a defined subset of code—with remaining assumptions made explicit.
It compiles to native code. Linear ownership makes resource management explicit and compiler-checked; its verification tools attach Lean-checked evidence to supported code. Proofs, tests, runtime checks and trusted assumptions remain distinct. The compiler itself is written in Lean 4.
Status: experimental. The language, standard library and tooling are still evolving. Concrete is not a fully verified compiler, and a successful build is not a proof that a program is correct. See Claims Today for the guarantees and boundaries, and the roadmap for planned work.
This complete program writes a message, checks the write and close results, and
explicitly releases its resources. Save it as hello/src/main.con; the setup
commands are below.
mod hello {
import std.io.{Writer, IoError, console_writer};
// The helper declares the authority required by its writer.
fn emit<cap C>(out: &Writer<C>, message: &String)
with(C) -> Result<u64, IoError> {
return out.write_str(message);
}
fn main() with(Console, Alloc) -> Int {
let message: String = "Hello, Concrete!\n";
let out: Writer<Console> = console_writer();
let written: u64 = emit(&out, &message).unwrap_or(0);
let complete: bool = written == message.len();
message.drop();
let closed: bool = out.close().is_ok();
if complete && closed { return 0; }
return 1;
}
}
Three things are visible in the code:
- Ownership:
emitborrows the writer and message.mainowns them, so it must consume or transfer them. Here,drop()releases the string andclose()consumes the writer. There is no implicit scope-exit destruction. - Authority:
emitrequires its writer's capabilityC. With this console writer, that meansConsole. RemovingConsolefrommainis a compiler error; passing the writer through a helper does not hide the requirement.Allocaccounts for the allocated string. - Failure: writing and closing return
Result. This program exits with 1 on a failed or short write, or a failed close. Closing happens even when the write fails.
The same helper can borrow a Writer<File> or a fixed-buffer Writer<{}>.
The required authority changes with the type; ownership and error handling
remain explicit.
The repository provides a Nix development shell with Lean, clang and the supporting tools. From a terminal:
git clone https://github.com/unbalancedparentheses/concrete2.git
cd concrete2
nix --extra-experimental-features "nix-command flakes" develop
lake build
export PATH="$PWD/.lake/build/bin:$PATH"If you already have Lean and clang installed, you can build outside Nix with
lake build. Use the exact Lean version in lean-toolchain,
currently 4.28.0, rather than an arbitrary newer version.
Create a project inside the checkout:
mkdir -p hello/src
cat > hello/Concrete.toml <<'EOF'
[package]
name = "hello"
version = "0.1.0"
EOFSave the program above in hello/src/main.con, then run:
cd hello
concrete build . -o hello
./hello
concrete src/main.con --report caps
concrete src/main.con --report unsafeThe program prints Hello, Concrete!. The reports show declared authority and
its supporting assumptions and coverage. An incomplete report is not evidence
that no assumptions exist.
A manifest matters: standard-library imports use project mode. For the
standalone-file workflow, see
Standalone File vs Project Mode.
Run concrete --help for commands, or explore an existing program such as
base64_cli.
| Concern | Concrete's approach |
|---|---|
| Resource ownership | Non-Copy values must be consumed or transferred; use after move and silent discard are rejected. |
| Borrowing | Safe references provide scoped access and cannot be returned from safe APIs. Mutable access is exclusive. |
| External authority | Callers declare the capabilities their calls require, including calls through capability-bearing handles and function pointers. |
| Cleanup | Explicit consuming calls, optionally scheduled with defer; failures abort rather than unwind. |
| Runtime safety | Safe indexing and ordinary integer arithmetic have bounds and overflow checks. Explicit wrapping and saturating operations have their named behavior. |
| Recoverable failure | APIs use values such as Result; failure paths must also respect ownership. |
A capability is an allowance, not a promise that every execution uses it.
with(File) does not identify a particular file or confine filesystem paths.
An empty capability set does not establish purity, termination, or functional
correctness: a function can still mutate an argument through &mut.
Foreign declarations are audited claims. A trusted body can discharge
Unsafe obligations, but must still declare its external authority. The
compiler checks callers against those declarations; it cannot prove a dishonest
C binding honest. Read the FFI rules and
handle capability design for the boundary.
Contracts state properties to establish. For example:
#[requires(0 <= i && i < 16)]
#[ensures(result == a[i])]
fn get16(a: [u8; 16], i: i32) -> u8 {
return a[i];
}
The precondition is an obligation for callers. The postcondition states what the function should return. Writing either annotation does not, by itself, prove it. Concrete generates obligations and reports the evidence available for them.
| Evidence | What it tells you |
|---|---|
| Compiler enforcement | A structural check, such as ownership or capability checking, accepted the code. |
| Lean proof | A kernel checked a stated theorem about the supported formal model. |
| External solver result | A solver discharged an obligation; solver trust remains explicit unless independently replayed. |
| Oracle test | Executions agreed with a reference implementation on tested inputs. |
| Runtime check | A condition is checked when the program executes. |
| Assumption or trusted boundary | The conclusion relies on something outside the checked proof. |
A theorem about extracted semantics is not an end-to-end proof of the native binary. Source-to-model correspondence, foreign code, the backend and the runtime have their own boundaries. Missing, stale and incomplete evidence must be read alongside successful results.
Start with the verification status and proof semantics boundary. The verification charter describes the longer-term direction separately from current support.
| Example | What to look for |
|---|---|
| Base64 CLI | Owned strings and bytes, explicit cleanup, and capability-bearing writers in a real program. |
| Constant-time tag comparison | Value-correctness proofs, with machine-level timing assumptions kept separate. |
| HMAC/SHA-256 | Refinement proofs and independent oracle tests supporting different claims. |
| Contract negatives | Invalid and unsupported claims that the tools must refuse. |
| VC examples | Bounds, arithmetic and contract obligations. |
The example inventory distinguishes tested examples from exploratory workloads. The documentation index links the language, standard library, compiler and verification references.
Inside the development shell, from the repository root:
lake build
bash scripts/tests/run_tests.sh
bash scripts/tests/check_doc_snippets.shThe full suite is substantial. For a focused change, consult the test guide for the relevant checks. Documentation code blocks are checked too, including the project example in this README.
For the design rationale, read Why Concrete?. For current priorities and release plans, use the roadmap. Questions and discussion are welcome in Telegram.
Concrete was originally specified and created by Federico Carrone at LambdaClass.
