Verifying Generics and Delegates · IntroductionGenericsDelegate clientsCapturing delegates Setup C# subset Basic imperative features + generics + delegates Assertion logic Logic

Post on 03-Oct-2020

1 Views

Category:

Documents

0 Downloads

Preview:

Click to see full reader

Transcript

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

top related