310 likes | 435 Views
Program synthesis with Jennisys. K. Rustan M. Leino Research in Software Engineering ( RiSE ), Microsoft Research, Redmond Aleksandar Milicevic MIT. IFIP Working Group 2.3 meeting Winchester, UK 22 September 2011. Post-mortem verification. Forward-looking design. Verification. Test.
E N D
Program synthesis with Jennisys K. Rustan M. Leino Research in Software Engineering (RiSE), Microsoft Research, Redmond Aleksandar Milicevic MIT IFIP Working Group 2.3 meeting Winchester, UK 22 September 2011
Post-mortem verification Forward-looking design Verification Test Code Idea Timeline Ouch! Need specifications
More help during software design • More expressive languages • Refinement • Synthesis • …
Jennisys This is where programs begin
Jennisys programs • Each type has: • Public interface • Data model • Code
Example: Public interface interfaceExtensibleArray[T] { varContents:seq[T] constructorInit() Contents := [] method Get(i) returns (t) requires0 <= i && i < |Contents| t := Contents[i] method Set(i, t) requires0 <= i && i < |Contents| Contents := Contents[i := t] methodAppend(t) Contents := Contents + [t] }
ExtensibleArray data structure Append( ) Extensible-Array[T] .elements
ExtensibleArray data structure Append( ) Extensible-Array[T] .elements
ExtensibleArray data structure Append( ) Extensible-Array[T] .elements
ExtensibleArray data structure Append( ) Extensible-Array[T] .elements .more
ExtensibleArray data structure Extensible-Array[T] .elements .more ExtensibleArray[array[T]]
Example: Data structure design datamodelExtensibleArray[T] { varelements: array[T] varmore: ExtensibleArray[array[T]] frame elements, more, more.Contents[*] invariant elements.Length = 256 256 < |Contents| ==> more != null more.Contents[*].Length = 256 val M = if more = nullthen0else256 * |more.Contents| Contents[i] = elements[i – M] where i in M <= i Contents[i] = more.Contents[i / 256][i % 256] whereiini < M }
Example: Data structure design Can all operations be implemented with this design? datamodelExtensibleArray<T> { varelements: array<T> varmore: ExtensibleArray<array<T>>? frame elements, more, more.Contents[*] invariant elements.Length = 256 256 < |Contents| ==> more != null more.Contents[*].Length = 256 val M = ifmore = nullthen0else256 * |more.Contents| Contents[i] = elements[i – M] where i in M <= i Contents[i] = more.Contents[i / 256][i % 256] whereiini < M }
Example: Data structure design datamodelExtensibleArray<T> { varelements: array<T> varmore: ExtensibleArray<array<T>>? frame elements, more, more.Contents[*] invariant elements.Length = 256 256 < |Contents| ==> more != null more.Contents[*].Length = 256 val M = if more = nullthen0else256 * |more.Contents| Contents[i] = elements[i – M] where i in M <= i Contents[i] = more.Contents[i / 256][i % 256] whereiini < M } Is this good? |Contents| = 5 more ≠ null |more.Contents| = 100
Example: Implementation Code is generated from public interface and data model codeExtensibleArray[T] { } • Code generated automatically • Programmer supplies hints • E.g., “loop n”, “e[n] := t” • Programmer uses sketches, holes[Bodik, Solar-Lezama, …] • As last resort, code is written manually
Jennisys, abstractly interface T { var a constructorInit() S(a) } datamodel T { var c invariant R(a, c) }
Jennisys, abstractly interface T { var a constructorInit() a := E } datamodel T { var c invariant R(a, c) }
Synthesis basics:Constraint solving interface T { var a constructorInit() a := E } var a, c a := E c :[ R(a, c) ] datamodel T { var c invariant R(a, c) } constraint solve to find feasible a,c here
Synthesis basics:Constraint solving interface T { var a constructorInit() a := E } var a, c a := E assume R(a, c) assert false datamodel T { var c invariant R(a, c) } attempt to verify and look at resulting counterexample model
Demo a := 0 with a = c a := 0 with a = c+d
Extrapolation interface T { var a constructorInit(p) a := E(p) } Constraint solving gives possible values for a,p,c From this, we want to extrapolate a value for c in terms of p datamodel T { var c invariant R(a, c) }
Extrapolation: Example interface T { var a constructorInit(p) a := p } Sample values:a=7, p=7, c=7 Match up c with p datamodel T { var c invariant a = c }
Custom spec evaluation interface T { var a constructorInit(p,q) a := {p + q} } Partially evaluate spec with the sample values for non-parameters Match things up datamodel T { var c invariant a = {c} } a={7}, p=3, q=4, c=7 {7} = {p+q}, {7} = {7}
Demo a := p+q with a = c a := {p+q} with a = {c}
Program extrapolation, so far • Constraint solving: get sample values • Partial evaluation: simplify spec using samples values • Unification: match things up • What if it doesn’t work?
Inferring branch structure • Program extrapolation • Attempt to verify • If resulting program does not verify: • Infer the needed guard using custom spec evaluation • Repeat synthesis for remaining cases
Branch structure: Example interface T { var a constructorInit(p,q) a := {p, q} } a={7}, p=3, q=4, c=3, d=4 Match c,d with p,q if (p≤q) { c,d := p,q } else { c,d := q,p } datamodel T { var c, d invariant a = {c,d} c ≤ d } Works only if p≤q Generate if Repeat assuming ¬(p≤q)
Objects • Each interface denotes an instantiable type, that is, a class of objects • A data model can also make use of objects
Demo SimpleCell
Delegation • An interface has model fields • part of the specification • not part of compiled code • If type X uses objects of type Y, its code should: • not set Y’s model fields directly, but • use Y’s interface to call constructors and methods to achieve the desired result
Conclusions • Synthesis by combination of: • Constraint solving • Symbolic/concrete evaluation • Unification • More to do: • Methods • Formalization, better understand the technique • … • Reflection:Is this how we should be programming?