Introduction Generics Delegate clients Capturing delegates
Verifying Generics and Delegates
Kasper SvendsenLars Birkedal
Matthew Parkinson
ECOOP 2010
June 23, 2010
Introduction Generics Delegate clients Capturing delegates
Introduction
HO separation logic for C# subset with generics and delegates
• Hoare-style program logic for proving functional correctness
• Separation logic for modular reasoning about state
In HOSL & Hoare Type Theory one can reason about
• polymorphism using universal quantification over predicates
• first-class functions using nested Hoare triples
We extend these techniques to reason about C#
• Main challenge: C# variable capture
Introduction Generics Delegate clients Capturing delegates
C# variable capture
Example
public delegate Y Func〈Y〉 ();
Func〈int〉 counter() {int x = 0;return delegate () { return ++x; };}
C# semantics
• Inline delegate captures the location of x
• Lifetime of captured x extended to lifetime of delegate
Introduction Generics Delegate clients Capturing delegates
Outline
Introduction
Generics
Delegate clients
Capturing delegates
Introduction Generics Delegate clients Capturing delegates
Setup
C# subset
• Basic imperative features + generics + delegates
Assertion logic
• Logic for reasoning about computational states
• Higher-order separation logic
• Spacial connectives, emp, ∗,−∗, for controlling aliasing
• State assertions, M.f 7→ N, ..., for describing state
Specification logic
• Logic for relating initial and terminal state
Introduction Generics Delegate clients Capturing delegates
Example – Integer list
class Node {Node next;Integer val;}
• Representation predicate
list(x , ε)def= x = null
list(x , a · α)def= ∃n,∃v . x .next 7→ n ∗ x .val 7→ v ∗
Int(v , a) ∗ list(n, α)
• Int(v , a): v is a representation of the integer a
Introduction Generics Delegate clients Capturing delegates
Example – Integer list
class Node {Node next;Integer val;}
• Representation predicate
list(x , ε)def= x = null
list(x , a · α)def= ∃n,∃v . x .next 7→ n ∗ x .val 7→ v ∗
Int(v , a) ∗ list(n, α)
• Int(v , a): v is a representation of the integer a
Introduction Generics Delegate clients Capturing delegates
Example – Generic list
class Node〈X〉 {Node〈X〉 next;X val;}
• Representation predicate
list(x , ε,P)def= x = null
list(x , a · α,P)def= ∃n,∃v . x .next 7→ n ∗ x .val 7→ v ∗
P(v , a) ∗ list(n, α,P)
• P(v , a): v is a representation of a
Introduction Generics Delegate clients Capturing delegates
Example – Fold
void fold〈X〉(Node〈X〉 lst, Action〈Node〈X〉〉 f) {if(lst != null) {
Node〈X〉 next = lst.next;f(lst);fold(next, f);}}
• foreach
• Stateful fold-right – accumulator maintained by delegate
Introduction Generics Delegate clients Capturing delegates
Example – Fold
void fold〈X〉(Node〈X〉 lst, Action〈Node〈X〉〉 f) {if(lst != null) {
Node〈X〉 next = lst.next;f(lst);fold(next, f);}}
• foreach
• Stateful fold-right – accumulator maintained by delegate
Introduction Generics Delegate clients Capturing delegates
Example – Fold
· · ·
Q(ε) ∗ list(lst, a1 · a2 · a3 · α, P )
lst
• Q(α): accumulator predicate; state after having folded over α
Introduction Generics Delegate clients Capturing delegates
Example – Fold
· · ·
Q(a1 · ε) ∗ list(lst, a2 · a3 · α, P )
lst
• Q(α): accumulator predicate; state after having folded over α
Introduction Generics Delegate clients Capturing delegates
Example – Fold
· · ·
list(lst, a3 · α, P )Q(a2 · a1 · ε) ∗
lst
• Q(α): accumulator predicate; state after having folded over α
Introduction Generics Delegate clients Capturing delegates
Example – Fold
Specification
void fold〈X〉(Node〈X〉 lst, Action〈Node〈X〉〉 f) { ... }∀α : Val. ∀P : Val× Val→ Prop. ∀Q : Val→ Prop.
{list(lst, α,P) ∗Q(ε) ∗ ∀a, β, y : Val.
f 7→ 〈(x). {x .next 7→ ∗ x .val 7→ y ∗ P(y , a) ∗Q(β)}{Q(a · β)}〉}
{Q(rev(α))}
• Q(α): accumulator predicate; state after having folded over α
• rev(α): mathematical reverse function
• M 7→ 〈(φ).{P} {Q}〉: M denotes delegate satisfying spec
Introduction Generics Delegate clients Capturing delegates
Example – Fold
Specification
void fold〈X〉(Node〈X〉 lst, Action〈Node〈X〉〉 f) { ... }∀α : Val. ∀P : Val× Val→ Prop. ∀Q : Val→ Prop.
{list(lst, α,P) ∗Q(ε) ∗ ∀a, β, y : Val.
f 7→ 〈(x). {x .next 7→ ∗ x .val 7→ y ∗ P(y , a) ∗Q(β)}{Q(a · β)}〉}
{Q(rev(α))}
• Q(α): accumulator predicate; state after having folded over α
• rev(α): mathematical reverse function
• M 7→ 〈(φ).{P} {Q}〉: M denotes delegate satisfying spec
Introduction Generics Delegate clients Capturing delegates
Variable capture
Increment example
int x = 0;Action f = delegate () {
x++;
};
f();
Introduction Generics Delegate clients Capturing delegates
Variable capture
Increment example
int x = 0;Action f = delegate () {
{ x = n }x++;{ x = n + 1 }
};{ x = 0 ∗ ∀n. f 7→ 〈{ x = n } { x = n + 1 }〉 }
f();
Introduction Generics Delegate clients Capturing delegates
Variable capture
Increment example
int x = 0;Action f = delegate () {
{ x = n }x++;{ x = n + 1 }
};{ x = 0 ∗ ∀n. f 7→ 〈{ x = n } { x = n + 1 }〉 }
f(); ⇒ f 7→〈{ 0 = 0 } { 0 = 1 }〉
Introduction Generics Delegate clients Capturing delegates
Variable capture
Increment example
int x = 0;Action f = delegate () {
{ emp }x++;{ emp }
};{ x = 0 ∗ f 7→ 〈{ emp } { emp }〉 }
f();
Introduction Generics Delegate clients Capturing delegates
Variable capture
Increment example
int x = 0;Action f = delegate () {
{ emp }x++;{ emp }
};{ x = 0 ∗ f 7→ 〈{ emp } { emp }〉 }
f();
Issues
• How to refer to captured variables in nested specs
• How to keep track of potentially modified variables
Introduction Generics Delegate clients Capturing delegates
Variable capture
Variable assertions
• Extend assertion logic with variable assertions Ms7→ N,&x
• Ms7→ N: location M is allocated on the stack and contains N
• &x : denotes location of program variable x
Inline delegate
int x = 0;Action f = delegate () {{ P }
B{ Q }
};{ x = 0 ∗ f 7→ 〈{ ∃y. &x
s7→ y ∗ [y/x]P } { ∃y. &xs7→ y ∗ [y/x]Q }〉 }
Introduction Generics Delegate clients Capturing delegates
Variable capture
Variable assertions
• Extend assertion logic with variable assertions Ms7→ N,&x
• Ms7→ N: location M is allocated on the stack and contains N
• &x : denotes location of program variable x
Inline delegate
int x = 0;Action f = delegate () {{ P }
B{ Q }
};{ x = 0 ∗ f 7→ 〈{ ∃y. &x
s7→ y ∗ [y/x]P } { ∃y. &xs7→ y ∗ [y/x]Q }〉 }
Introduction Generics Delegate clients Capturing delegates
Variable capture
Variable assertions
• Extend assertion logic with variable assertions Ms7→ N,&x
• Ms7→ N: location M is allocated on the stack and contains N
• &x : denotes location of program variable x
Inline delegate
int x = 0;Action f = delegate () {{ x = n }
x++;{ x = n + 1 }
};{ x = 0 ∗ ∀n. f 7→ 〈{ &x
s7→ n } { &xs7→ n + 1 }〉 }
Introduction Generics Delegate clients Capturing delegates
Variable capture
Variable assertions
• Extend assertion logic with variable assertions Ms7→ N,&x
• Ms7→ N: location M is allocated on the stack and contains N
• &x : denotes location of program variable x
Inline delegate
int x = 0;Action f = delegate () {{ emp }
x++;{ emp }
};{ x = 0 ∗ f 7→ 〈{ ∃y. &x
s7→ y } { ∃y. &xs7→ y }〉 }
Introduction Generics Delegate clients Capturing delegates
Variable capture
Aliasing
• Var. assertions introduce aliasing in reasoning about variables:
• Build separation into specification logic:• Can either reason directly or indirectly, but not both• Reason directly about variables in the program var. ctx. φ
• Hoare’s assignment rule thus stil sound
φ;ψ ` P : Prop x , y ∈ φφ;ψ ` {P[y/x ]}x := y{P}
Introduction Generics Delegate clients Capturing delegates
Variable capture
Aliasing
• Var. assertions introduce aliasing in reasoning about variables:
• Build separation into specification logic:• Can either reason directly or indirectly, but not both• Reason directly about variables in the program var. ctx. φ• Hoare’s assignment rule thus stil sound
φ;ψ ` P : Prop x , y ∈ φφ;ψ ` {P[y/x ]}x := y{P}
Introduction Generics Delegate clients Capturing delegates
Variable capture
Capturing delegates
• Verify body using Hoare treatment of variables
• Switch to SL treatment of captured variables in nested spec
• Switch back to Hoare treatment of variables to verify calls
x 6∈ FV (s)
φ;ψ ` {∃x : Val. ls7→ x ∗ P}s{∃x : Val. l
s7→ x ∗Q}φ, x ;ψ ` {&x = l ∧ P}s{Q}
Introduction Generics Delegate clients Capturing delegates
Variable capture
Capturing delegates
• Verify body using Hoare treatment of variables
• Switch to SL treatment of captured variables in nested spec
• Switch back to Hoare treatment of variables to verify calls
x 6∈ FV (s)
φ;ψ ` {∃x : Val. ls7→ x ∗ P}s{∃x : Val. l
s7→ x ∗Q}φ, x ;ψ ` {&x = l ∧ P}s{Q}
Introduction Generics Delegate clients Capturing delegates
Example – In-place reverse
Node〈X〉 reverse〈X〉(Node〈X〉 lst) {Node〈X〉 head = null;fold〈X〉(lst, delegate (Node〈X〉 x) { x.next = head; head = x; });return head;}
Introduction Generics Delegate clients Capturing delegates
Example – In-place reverse
Node〈X〉 reverse〈X〉(Node〈X〉 lst) {Node〈X〉 head = null;fold〈X〉(lst, delegate (Node〈X〉 x) { x.next = head; head = x; });return head;}
· · ·
head lst
Introduction Generics Delegate clients Capturing delegates
Example – In-place reverse
Node〈X〉 reverse〈X〉(Node〈X〉 lst) {Node〈X〉 head = null;fold〈X〉(lst, delegate (Node〈X〉 x) { x.next = head; head = x; });return head;}
null · · ·
head lst
Introduction Generics Delegate clients Capturing delegates
Example – In-place reverse
Node〈X〉 reverse〈X〉(Node〈X〉 lst) {Node〈X〉 head = null;fold〈X〉(lst, delegate (Node〈X〉 x) { x.next = head; head = x; });return head;}
null · · ·
head lst
Introduction Generics Delegate clients Capturing delegates
Example – In-place reverse
Node〈X〉 reverse〈X〉(Node〈X〉 lst) {Node〈X〉 head = null;fold〈X〉(lst, delegate (Node〈X〉 x) { x.next = head; head = x; });return head;}
∀α : Val. ∀P : Val× Val→ Prop.
{list(lst, α,P)}{r .list(r , rev(α),P)}
Introduction Generics Delegate clients Capturing delegates
Example – In-place reverse
Node〈X〉 reverse〈X〉(Node〈X〉 lst) {Node〈X〉 head = null;fold〈X〉(lst, delegate (Node〈X〉 x) { x.next = head; head = x; });return head;}
· · ·· · ·
Q(β) ∗ list(lst, a · α, P )
head
where Q(α) = ∃n : Val. &heads7→ n ∗ list(n, α,P)
Introduction Generics Delegate clients Capturing delegates
Example – In-place reverse
Node〈X〉 reverse〈X〉(Node〈X〉 lst) {Node〈X〉 head = null;fold〈X〉(lst, delegate (Node〈X〉 x) { x.next = head; head = x; });return head;}
· · ·· · ·
Q(a · β) list(lst, α, P )∗
head
where Q(α) = ∃n : Val. &heads7→ n ∗ list(n, α,P)
Introduction Generics Delegate clients Capturing delegates
Conclusion
Generics and non-capturing delegates
• HOL & nested Hoare triples (standard)
Capturing delegates
• Separation logic treatment of variables
• Variable separation build into specification logic
• Unified treatment of local state on the heap and/or stack
• Reasoning standard when there is no capturing