Program synthesis with Jennisys K. Rustan M. Leino Research in Software Engineering (RiSE),...

Preview:

Citation preview

Program synthesis with Jennisys

K. Rustan M. LeinoResearch in Software Engineering (RiSE), Microsoft Research, Redmond

Aleksandar MilicevicMIT

IFIP Working Group 2.3 meetingWinchester, UK22 September 2011

Post-mortem verification

TimelineIdea

CodeTest Verifi

catio

n

Ouch!

Need

specifications

Forward-looking design

More help during software designMore expressive languages

RefinementSynthesis…

This is where programs begin

Jennisys

Jennisys programs

Each type has:Public interfaceData modelCode

Example: Public interfaceinterface ExtensibleArray[T] { var Contents: seq[T]  constructor Init() Contents := []  method Get(i) returns (t) requires 0 <= i && i < |Contents| t := Contents[i]  method Set(i, t) requires 0 <= i && i < |Contents| Contents := Contents[i := t]  method Append(t) Contents := Contents + [t]}

ExtensibleArray data structure

Extensible-

Array[T]

Append( )

.elements

ExtensibleArray data structure

Extensible-

Array[T]

Append( )

.elements

ExtensibleArray data structure

Extensible-

Array[T]

Append( )

.elements

ExtensibleArray data structure

Extensible-

Array[T]

Append( )

.elements

.more

ExtensibleArray data structure

Extensible-

Array[T] .elements

.more

ExtensibleArray

[array[T]]

Example: Data structure designdatamodel ExtensibleArray[T] {

 var elements: array[T]  var more: ExtensibleArray[array[T]]   frame   elements, more, more.Contents[*]   invariant  elements.Length = 256  256 < |Contents| ==> more != null  more.Contents[*].Length = 256   val M = if more = null then 0 else 256 * |more.Contents|  Contents[i] = elements[i – M] where i in M <= i   Contents[i] = more.Contents[i / 256][i % 256] where i in i < M}

Example: Data structure designdatamodel ExtensibleArray<T> {

 var elements: array<T>  var more: ExtensibleArray<array<T>>?   frame   elements, more, more.Contents[*]   invariant  elements.Length = 256  256 < |Contents| ==> more != null  more.Contents[*].Length = 256   val M = if more = null then 0 else 256 * |more.Contents|  Contents[i] = elements[i – M] where i in M <= i   Contents[i] = more.Contents[i / 256][i % 256] where i in i < M}

Can all operations be

implemented with

this design?

Example: Data structure designdatamodel ExtensibleArray<T> {

 var elements: array<T>  var more: ExtensibleArray<array<T>>?   frame   elements, more, more.Contents[*]   invariant  elements.Length = 256  256 < |Contents| ==> more != null  more.Contents[*].Length = 256   val M = if more = null then 0 else 256 * |more.Contents|  Contents[i] = elements[i – M] where i in M <= i   Contents[i] = more.Contents[i / 256][i % 256] where i in i < M}

Is this good?|Contents| = 5more ≠ null|more.Contents| = 100

Example: Implementation

code ExtensibleArray[T] {

}Code is

generated

from public

interface and

data modelCode generated automaticallyProgrammer supplies hints

E.g., “loop n”, “e[n] := t”Programmer uses sketches, holes[Bodik, Solar-Lezama, …]

As last resort, code is written manually

Jennisys, abstractlyinterface T {

var aconstructor

Init()S(a)

}

datamodel T {var cinvariant R(a,

c)}

Jennisys, abstractlyinterface T {

var aconstructor

Init()a := E

}

datamodel T {var cinvariant R(a,

c)}

Synthesis basics:

Constraint solvinginterface T {

var aconstructor

Init()a := E

}

datamodel T {var cinvariant R(a,

c)}

var a, c

a := Ec :[ R(a, c) ]

constraint solve to find feasible a,c

here

Synthesis basics:

Constraint solvinginterface T {

var aconstructor

Init()a := E

}

datamodel T {var cinvariant R(a,

c)}

var a, c

a := Eassume R(a, c)assert false

attempt to verify and look

at resulting counterexampl

e model

a := 0 with a = ca := 0 with a = c+d

Demo

Extrapolationinterface T {

var aconstructor

Init(p)a := E(p)

}

datamodel T {var cinvariant R(a, c)

}

Constraint solving gives possible values for a,p,c

From this, we want to extrapolate a value for c in terms of p

Extrapolation: Exampleinterface T {

var aconstructor

Init(p)a := p

}

datamodel T {var cinvariant a = c

}

Sample values:a=7, p=7, c=7

Match up c with p

Custom spec evaluationinterface T {

var aconstructor

Init(p,q)a := {p + q}

}

datamodel T {var cinvariant a = {c}

}

Partially evaluate spec with the sample values for non-parameters

Match things upa={7}, p=3, q=4,

c=7

{7} = {p+q}, {7} = {7}

a := p+q with a = ca := {p+q} with a = {c}

Demo

Program extrapolation, so far

Constraint solving: get sample valuesPartial evaluation: simplify spec using samples valuesUnification: match things up

What if it doesn’t work?

Inferring branch structure

Program extrapolationAttempt to verifyIf resulting program does not verify:

Infer the needed guard using custom spec evaluationRepeat synthesis for remaining cases

Branch structure: Exampleinterface T {

var aconstructor

Init(p,q)a := {p, q}

} datamodel T {

var c, dinvariant a =

{c,d} c ≤ d

}

a={7}, p=3, q=4, c=3, d=4

Match c,d with p,qWorks only if p≤qGenerate ifRepeat assuming ¬(p≤q)

if (p≤q) {c,d := p,q

} else {c,d := q,p

}

Objects

Each interface denotes an instantiable type, that is, a class of objectsA data model can also make use of objects

SimpleCell

Demo

Delegation

An interface has model fieldspart of the specificationnot part of compiled code

If type X uses objects of type Y, its code should:

not set Y’s model fields directly, butuse Y’s interface to call constructors and methods to achieve the desired result

ConclusionsSynthesis by combination of:

Constraint solvingSymbolic/concrete evaluationUnification

More to do:MethodsFormalization, better understand the technique…

Reflection:Is this how we should be programming?

Recommended