Deploy Chalice to Europe β K. Rustan M. Leino πΊπΈ + Peter MΓΌller π¨π + ETH Zurich (2009), the Object-Oriented Research Language that First Implemented Implicit Dynamic Frames and Magic Wands, the Foundations of Viper, on EU Infrastructure in 2026
Before Viper had Silver. Before Prusti had #[requires]. Before Gobra had //@ acc(). There was a small research language at the intersection of Microsoft Research Redmond and ETH Zurich that first put all the pieces together.
Chalice is an object-oriented research programming language developed by K. Rustan M. Leino πΊπΈ (Microsoft Research Redmond) and Peter MΓΌller π¨π (ETH Zurich), first presented publicly around 2009 at workshops on verification and then described in detail in the VSTTE 2010 paper "Chalice: A Language for Modular Programs with Shared Mutable State." Chalice was not designed to be a production language. It was designed to answer a research question: can a single, coherent permission model handle both sequential and concurrent heap reasoning, in a language that looks like a real object-oriented program?
The answer was yes. And the permission model Chalice invented became the theoretical core of an entire European verification ecosystem.
Why Chalice Matters: Two Inventions That Changed Verification
Chalice introduced two ideas that are now foundational to the Viper cluster β used in every Viper front-end but never explained directly in the front-end documentation.
1. Implicit Dynamic Frames (IDF)
Classical separation logic requires explicit heap footprints. Every method specifies precisely which memory cells it reads and writes β this.balance β¦ v β using syntactic heap constructs. This is sound and precise, but verbose and difficult to connect to object-oriented programs where the heap structure emerges at runtime.
Implicit Dynamic Frames, introduced by Smans, Jacobs, and Piessens (KU Leuven π§πͺ, 2009) and independently operationalised in Chalice, replaces explicit heap terms with access permissions. Instead of writing "this method owns the heap cell for x.balance," you write:
method Deposit(amount: int)
requires acc(this.balance) && amount >= 0
ensures acc(this.balance) && this.balance == old(this.balance) + amount
{
this.balance := this.balance + amount
}
acc(this.balance) is not a heap term β it is a permission to read and write this.balance. The heap content is implicit behind the permission. If you hold acc(x.f), you may read and write x.f. If two permissions are in separating conjunction, the two heap regions are disjoint by construction.
The "dynamic" part: permissions can be transferred at runtime. You pass acc(x.f) to a called method; the method consumes it for its duration; you receive it back via ensures. The footprint evolves dynamically as your execution path changes β unlike static alias analysis or region-based type systems.
This is precisely the acc(x.f, p) you see in Viper, Prusti, Gobra, and Nagini. Every //@ requires acc(x.field) annotation in a Gobra Go program, every Acc(self.balance) call in a Nagini Python spec, every #[requires(acc(x.f))] in a Prusti Rust program traces its lineage to the IDF formalism Chalice first implemented.
2. Magic Wands
The second Chalice innovation was the magic wand β the separating implication operator A --* B.
A --* B means: if you provide me with a heap satisfying A, I will give you a heap satisfying B. This is not ordinary implication. The A and the current heap are combined in a separating-conjunctive sense: the heap the wand holds, plus the incoming A, together produce B. Wands let you reason about partial data structures β iterators, cursors, tree zippers, "holes" in a linked list.
The canonical use case: you are iterating over a linked list to sum its elements. At each step, you hold:
- The current node's permission (
acc(curr.val) && acc(curr.next)) - A wand that says "give me the remaining list back, and I'll give you the full sorted list" β this wand encodes the loop context
Without magic wands, you cannot verify iterators over recursive data structures without inlining the entire recursive predicate at each loop step. With wands, the hole in the data structure is a first-class resource.
Chalice was the first programming language to implement magic wands β not just in theory, but as a working verifier that could check programs using A --* B against a Boogie backend. The idea was later refined and added to Viper's Silver IL (A --* B appears in Viper specifications as acc(x.f) --* list(x)), where it is used by VerCors for concurrent data structure reasoning.
Chalice Language Semantics
Classes and Fields
Chalice is object-oriented with explicit field declarations:
class Account {
var balance: int
var owner: string
invariant acc(balance) && acc(owner) && balance >= 0
}
The invariant clause is a monitor invariant β it holds whenever no thread holds the monitor lock. When a thread acquires the lock, it gains the invariant's permissions and obligations. When it releases, it must re-establish the invariant. This is the Chalice encoding of the lock-coupling concurrency idiom.
Fractional Permissions
Chalice generalises from binary (exclusive/none) ownership to fractional permissions:
method ReadBalance() returns (result: int)
requires rd(this.balance) // read permission β fractional, shareable
ensures rd(this.balance)
ensures result == this.balance
{
result := this.balance
}
method WriteBalance(newVal: int)
requires acc(this.balance) // full permission β exclusive, non-shareable
ensures acc(this.balance)
ensures this.balance == newVal
{
this.balance := newVal
}
rd(x.f) denotes a read permission β a fractional slice (0, 1) of the permission to x.f. Multiple threads can simultaneously hold rd(x.f), enabling safe concurrent reads. Only one thread can hold acc(x.f) (the full permission 1), enabling safe exclusive writes. This fractional permission discipline eliminates data races by proof, without locks, for read-dominant patterns.
The rd/acc split maps directly to what later became acc(x.f, 1/2) (read) and acc(x.f, 1) (write) in Viper β same semantics, cleaner syntax.
Predicates
Chalice predicates name heap regions, enabling abstraction over complex data structures:
predicate List(node: Node) {
node != null ==>
acc(node.val) && acc(node.next) && List(node.next)
}
method SumList(head: Node) returns (sum: int)
requires List(head)
ensures List(head)
{
unfold List(head)
if (head == null) {
sum := 0
} else {
var rest: int
rest := SumList(head.next)
sum := head.val + rest
}
fold List(head)
}
fold packs the unfolded heap region back into the abstract predicate. unfold does the inverse. This is exactly the fold/unfold mechanism in Viper's Silver IL β Chalice established the pattern, Viper productized it.
Channels and Permission Transfer
Chalice models Go-style message passing with permission annotations:
channel DataChannel(data: int)
where acc(sender.buffer)
method Producer(ch: DataChannel, buf: int)
requires acc(this.buffer)
{
this.buffer := buf
send ch(42) between acc(this.buffer) and emp
// after send: no longer hold acc(this.buffer)
}
method Consumer(ch: DataChannel)
ensures acc(receiver.buffer)
{
var v: int
receive v from ch
// after receive: hold acc(receiver.buffer)
}
The between ... and ... clause on send transfers a heap permission from sender to receiver across the channel. This is the formal mechanism for safe message-passing concurrency: the sender gives up ownership of the data when it sends; the receiver acquires ownership when it receives. No data race is possible because ownership is exclusive.
Gobra's goroutine/channel permission transfer (also developed at ETH Zurich) implements the same principle using the Viper IDF model β ch.send(data) with //@ requires acc(data) in Gobra is the Chalice channel pattern translated to Go.
Fork, Join, and Thread Annotations
method ParallelSum(left: Node, right: Node) returns (sum: int)
requires List(left) && List(right)
ensures List(left) && List(right)
{
var t: thread
var leftSum: int
var rightSum: int
fork t := SumList(left) // transfers List(left) to new thread
rightSum := SumList(right) // main thread handles right
join leftSum := t // reclaims List(left) from thread
sum := leftSum + rightSum
}
fork t := method(args) creates a thread and transfers the permissions in method's precondition to the new thread. join leftSum := t blocks until the thread completes and reclaims the postcondition's permissions. This is exactly the share/unshare + fork/join pattern in VerCors for concurrent Java β same semantics, different syntax.
Magic Wand Example: Iterator
method FindFirst(head: Node, target: int) returns (found: bool)
requires List(head)
ensures List(head)
{
var curr: Node := head
var wand: acc(List(curr)) --* List(head) // wand: "give me List(curr), get back List(head)"
while (curr != null)
invariant List(curr) && wand
{
unfold List(curr)
if (curr.val == target) {
fold List(curr)
apply wand // apply wand: consume List(curr), produce List(head)
found := true
return
}
var next: Node := curr.next
// extend wand to cover curr's node
wand := (acc(List(next)) --* List(head)) with {
fold List(curr)
apply wand
}
curr := next
}
apply wand
found := false
}
The wand acc(List(curr)) --* List(head) captures the "hole" between the iterator's current position and the list head. At each step, the wand is extended to cover one more node. When the iterator is done, applying the wand reassembles the full list. No recursive unrolling is required β the wand is the recursive context, carried as a resource.
The ETH Zurich Lineage: From Chalice to Viper
The genealogy of the Viper ecosystem runs through Chalice:
Chalice (2009)
β K. Rustan M. Leino + Peter MΓΌller (ETH Zurich π¨π)
β First IDF implementation. First magic wands. Boogie backend.
β "Can one permission model handle both concurrent and sequential OO?"
βΌ
Viper / Silver IL (2016)
β Peter MΓΌller + Malte Schwerhoff + Alexander Summers (ETH Zurich π¨π)
β Cleaned-up IDF. Productized infrastructure. Two backends: Silicon + Carbon.
β "Can one IVL serve multiple source languages simultaneously?"
βΌ
βββββββββββββββ¬ββββββββββββββ¬ββββββββββββββ¬ββββββββββββββ¬ββββββββββββββ
β Prusti β Gobra β Nagini β VeriFast β VerCors β
β Rust π¨π β Go π¨π β Python π¨π β C/Java π§πͺ β Java/C/GPU π³π±β
β (2016) β (2021) β (2018) β (2008) β (2017) β
βββββββββββββββ΄ββββββββββββββ΄ββββββββββββββ΄ββββββββββββββ΄ββββββββββββββ
Every acc(x.f, p) in Prusti, every //@ acc(x.field) in Gobra, every Acc(self.balance) in Nagini is the IDF permission model that first lived in Chalice. The fold/unfold predicates. The fractional rd/acc split. The channel permission transfer. The magic wand --*. All of it: Chalice first.
Peter MΓΌller is the connecting figure: Chalice co-author (Microsoft Research / ETH Zurich, 2009) β Viper co-creator (ETH Zurich, 2016) β Prusti co-creator (ETH Zurich, 2016) β Gobra co-creator (ETH Zurich, 2021) β Nagini co-author (ETH Zurich, 2018). One researcher, fifteen years, a complete EU verification ecosystem.
Backend: Boogie and the Shared Verification Infrastructure
Chalice uses Boogie as its verification backend β the same intermediate verification language that powers Viper's Carbon backend, Dafny, and SPARK Ada's GNATprove. Chalice β Boogie translation:
- Permission tracking: Chalice permission assertions are translated to Boogie state variables tracking which thread holds which permission amount for which heap location.
- Heap encoding: Object fields become uninterpreted Boogie functions:
Heap: ref Γ Field β Value, updated via Boogie map comprehensions. - Monitor invariants: Lock acquisition/release becomes Boogie
assume/assertpairs for the invariant. - Magic wands: Encoded as existential witnesses over Boogie states β the wand "holds" a snapshot of the heap that will be provided to the consumer.
Boogie discharges the resulting verification conditions via Z3 β the same SMT solver used by Viper Silicon's carbon backend, Dafny, and all other tools in this lineage. The Chalice β Boogie β Z3 pipeline was the proof-of-concept that the Viper β Silicon/Carbon β Z3 productization later scaled to multiple front-end languages.
EU Regulatory Alignment
EU AI Act Art. 9 (High-Risk System Technical Documentation): Chalice's fractional permission model β where read-sharing (rd) is statically bounded and exclusive write (acc) is non-shareable β provides a formal language for documenting concurrent AI inference system correctness. A Chalice-verified inference server proves that parallel read access to model weights is safe (multiple rd(model.weights) permissions held simultaneously) while parameter updates require exclusive ownership (acc(model.weights)) β precisely the concurrent access pattern EU AI Act Art. 9 requires evidence for in high-risk Annex III systems.
EU AI Act Art. 10 (Data Governance for High-Risk Systems): Chalice channel semantics β between acc(data) and emp on send, receive transfers ownership β formally model data pipeline ownership. A Chalice specification of an ETL pipeline proves that personal data transferred across processing stages is owned by exactly one stage at a time: no aliasing, no unintended sharing across processing boundaries.
GDPR Art. 25 (Data Protection by Design): Chalice monitor invariants can encode data minimisation constraints as heap ownership invariants: invariant acc(this.personalData) && this.personalData.fields.length <= allowedFields. Any thread accessing personalData must hold the monitor lock, which enforces the invariant β structural GDPR Art. 25 compliance by construction.
Cyber Resilience Act (CRA, 2027): Chalice's race-freedom by permission proof (no two threads hold conflicting permissions over the same heap cell) provides CRA Article 13 evidence for concurrent software components in EU digital products.
IEC 62304 Class C (Medical Device Software): Concurrent Java pipelines in medical imaging systems (MRI reconstruction, CT image processing) involve producer-consumer patterns that map directly to Chalice's fork/join + channel permission model β formal proofs of freedom from data corruption in concurrent medical device software.
Deploy Chalice on EU Infrastructure with sota.io
Chalice runs as a research tool via its GitHub distribution. The Chalice verifier is available as a JAR with a Boogie + Z3 dependency:
# Install prerequisites: Java 11+, Boogie, Z3
sudo apt-get install default-jdk z3
# Install Boogie (via dotnet)
dotnet tool install --global boogie
# Clone Chalice
git clone https://github.com/viperproject/chalice
cd chalice
# Verify a Chalice program
java -jar chalice.jar BankAccount.chalice
# With explicit Boogie path
java -jar chalice.jar --boogie /usr/local/bin/boogie Account.chalice
A simple Chalice program demonstrating the permission model:
class BankAccount {
var balance: int
method Init()
requires rd(balance)
ensures acc(balance) && balance == 0
{
balance := 0
}
method Deposit(amount: int)
requires acc(balance) && amount >= 0
ensures acc(balance) && balance == old(balance) + amount
{
balance := balance + amount
}
method ReadBalance() returns (b: int)
requires rd(balance)
ensures rd(balance) && b == balance
{
b := balance
}
}
Deploy a Chalice-integrated verification CI pipeline on sota.io β the EU-native PaaS on German infrastructure, GDPR-compliant by default:
# sota.io deploy
sota deploy --region eu-central
sota.io provides:
- EU data residency β Chalice verification artifacts, proof obligation results, and correctness evidence stay in Germany. No US Cloud Act exposure for source code or verification metadata.
- Managed PostgreSQL β store Chalice verification history, permission proof statistics, and correctness evidence per commit β traceable for IEC 62304 / IEC 61508 compliance artifacts.
- Zero DevOps β push your Chalice CI container and sota.io handles deployment, scaling, and database backups.
- GDPR compliance β data processed in EU jurisdiction. Research and industrial source code never leaves European infrastructure.
Free tier available. No credit card required to start.
See Also
- Deploy Viper to Europe β β Chalice's direct successor: Permission-based IVL (ETH Zurich π¨π, 2016) that productized IDF and magic wands into a shared backend
- Deploy Prusti to Europe β β Viper-based Rust verifier (ETH Zurich π¨π), IDF permissions from Chalice lineage
- Deploy Gobra to Europe β β Viper-based Go verifier (ETH Zurich π¨π), channel permission transfer descended from Chalice channels
- Deploy Nagini to Europe β β Viper-based Python verifier (ETH Zurich π¨π),
Acc()permissions from IDF - Deploy VeriFast to Europe β β Separation logic C/Java verifier (KU Leuven π§πͺ), co-developed IDF with Chalice simultaneously
- Deploy VerCors to Europe β β Viper-based concurrent Java/C/OpenCL verifier (TU Twente π³π±), uses magic wands for concurrent data structure reasoning