marp | theme | style |
---|---|---|
true |
default |
img[alt~="center"] {
display: block;
margin: 0 auto;
}
|
Nico Lehmann, Adam Geller, Gilles Barthe, Niki Vazou, Ranjit Jhala
Types vs. Floyd-Hoare logic
Flux - Liquid Types for Rust
Flux v. Prusti for Memory safety
type Nat = {v: Int | 0 <= v}
Int
is the base type of the valuev
names the value being described0 <=v
is a predicate constraint
Generate the sequence of values between lo
and hi
range :: lo:Int -> {hi:Int | lo <= hi} -> List {v:Int|lo <= v && v < hi}
Generate the sequence of values between lo
and hi
range :: lo:Int -> {hi:Int | lo <= hi} -> List {v:Int|lo <= v && v < hi}
lo <= hi
Generate the sequence of values between lo
and hi
range :: lo:Int -> {hi:Int | lo <= hi} -> List {v:Int|lo <= v && v < hi}
Every element in sequence is between lo
and hi
Types decompose (quantified) assertions to quantifier-free refinements
Floyd-Hoare requires elements
and quantified axioms
Liquid Parametric polymorphism yields spec for free
Floyd-Hoare Quantified postcondition (hard to infer)
Liquid Quantifier-free type (easy to infer)
Int -> k:Int -> List {v:Int| k <= v}
Types decompose assertions to quantif-free refinements ...
... but what about imperative programs
Types vs. Floyd-Hoare logic
Flux
Liquid Types for Rust
Flux
v. Prusti
for Memory Safety
flux
(/flʌks/)
n. 1 a flowing or flow. 2 a substance used to refine metals. v. 3 to melt; make fluid.
Types vs. Floyd-Hoare logic
Flux
Liquid Types for Rust
Flux
v. Prusti
for Memory Safety
Flux
v. Prusti
for Memory Safety
// Rust
fn store(&mut self, idx: usize, value: T)
// Flux
fn store(self: &mut RVec<T>[@n], idx: usize{idx < n}, value: T)
// Prusti
requires(index < self.len())
ensures(self.len() == old(self.len()))
ensures(forall(|i:usize| (i < self.len() && i != index) ==>
self.lookup(i) == old(self.lookup(i))))
ensures(self.lookup(index) == value)
Quantifiers make SMT slow!
fn kmeans(n:usize, cs: k@RVec<RVec<f32>[n]>, ps: &RVec<RVec<f32>[n]>, iters: i32) -> RVec<RVec<f32>[n]>[k] where 0 < k
-
Point is an
n
dimensional float-vecRVec<f32>[n]
-
Centers are a vector of
k
pointsRVec<RVec<f32>[n]>[k]
ensures(forall(|i:usize| (i < self.len() && i != index) ==>
self.lookup(i) == old(self.lookup(i))))
Have to duplicate code in new (untrusted) wrapper library
#[requires(i < self.rows() && j < self.cols())]
#[ensures(self.cols() == old(self.cols()) && self.rows() == old(self.rows()))]
pub fn set(&mut self, i: usize, j: usize, value: T) {
self.inner[i][j] = value;
}
flux
infers quantifier-free refinements via Horn-clauses/Liquid Typing
// Prusti
body_invariant!(forall(|x: usize| x < t.len() ==> t.lookup(x) < pat_len));
// Flux
t: RVec<{v:v < pat_len}>
flux
infers quantifier-free refinements via Horn-clauses/Liquid Typing
Types vs. Floyd-Hoare logic
Flux
Liquid Types for Rust
Flux
v. Prusti
for Memory Safety
Refinements + Rust's Ownership = Ergonomic Imperative Verification...
-
Specify complex invariants by composing type constructors & QF refinements
-
Verify complex invariants by decomposing validity checks via syntactic subtyping
Refinements + Rust's Ownership = Ergonomic Imperative Verification...
-
Specify complex invariants by composing type constructors & QF refinements
-
Verify complex invariants by decomposing validity checks via syntactic subtyping
... But this is just the beginning
-
Flux
restricts specifications,Prusti
allows way more ... -
... how to stretch types to "full functional correctness"?
What are interesting application domains to focus on?