Upgrade to Pro — share decks privately, control downloads, hide ads and more …

An encoding of Any for Leon

An encoding of Any for Leon

We describe how we added support for Scala's top type `Any` to Leon, by encoding it as a sum type, and by lifting expressions into it.

Romain Ruetschi

July 14, 2015
Tweet

More Decks by Romain Ruetschi

Other Decks in Programming

Transcript

  1. An encoding of Any for Leon Romain Ruetschi, LARA -

    EPFL Spring 2015 0
  2. Motivation 1

  3. Proving properties of unityped programs def reverse(lst: Any): Any =

    { if (lst == Nil()) Nil() else reverse(lst.tail) ++ Cons(lst.head, Nil()) } ensuring (_.contents == lst.contents) def reverseReverseIsIdentity(lst: Any) = { reverse(reverse(lst)) == lst }.holds 2
  4. Implementation 3

  5. Example case class Box(value: Int) def double(x: Any): Any =

    x match { case n: Int => n * 2 case Box(n) => Box(n * 2) case _ => x } double(42) 4
  6. Example sealed abstract class Any1 case class Any1Int(value: Int) extends

    Any1 case class Any1Box(value: Box) extends Any1 def double(x: Any1): Any1 = x match { case Any1Int(n) => Any1Int(n * 2) case Any1Box(Box(n)) => Any1Box(Box(n * 2)) case _ => x } double(Any1Int(42)) 5
  7. Outline ￿ Declare constructors ￿ Rewrite functions types to replace

    Any with Any1 ￿ Rewrite pattern matches to peel the constructors off ￿ Lift expressions into Any1 ￿ Extract implicit classes as case classes ￿ Add extension methods to Any 6
  8. Declare constructors sealed abstract class Expr case class Lit(n: Int)

    extends Expr case class Add(l: Expr, r: Expr) extends Expr abstract class Any1 case class Any1Expr(value: Expr) extends Any1 case class Any1Int(value: Int) extends Any1 7
  9. Rewrite functions types def id(x: Any): Any = x case

    class Box(value: Any) { def map(f: Any => Any): Box = Box(f(value)) def contains(other: Any): Boolean = value == other } 8
  10. Rewrite functions types def id(x: Any1): Any1 = x case

    class Box(value: Any1) { def map(f: Any1 => Any1): Box = Box(f(value)) def contains(other: Any1): Boolean = value == other } 9
  11. Lift expressions def id(x: Any1): Any1 = x def toAny(x:

    Int): Any1 = x def double(x: Any1): Any1 = x match { case i: Int => x * 2 case b: Boolean => !b case _ => x } 10
  12. Lift expressions def id(x: Any1): Any1 = x def toAny(x:

    Int): Any1 = Any1Int(x) def double(x: Any1): Any1 = x match { case i: Int => Any1Int(i * 2) case b: Boolean => Any1Boolean(!b) case _ => x } 11
  13. Rewrite pattern matches def double(x: Any1): Any1 = x match

    { case Any1Int(i: Int) => Any1Int(i * 2) case Any1Boolean(b: Boolean) => Any1Boolean(!b) case _ => x } 12
  14. Extract implicit classes @library implicit class BooleanOps(val underlying: Boolean) {

    def holds : Boolean = { assert(underlying) underlying } def ==> (that: Boolean): Boolean = { !underlying || that } } 13
  15. Extract implicit classes @library case class BooleanOps(underlying: Boolean) { def

    holds: Boolean = { assert(underlying) underlying } def ==> (that: Boolean): Boolean = { !underlying || that } } @library def BooleanOps(x: Boolean): BooleanOps = BooleanOps(x) 14
  16. Add extension methods to Any @library implicit class Any1Ops(val lhs:

    Any) { def *(rhs: Any): Any = (lhs, rhs) match { case (l: Int, r: Int) => l * r case (l: BigInt, r: BigInt) => l * r case (l: String, r: BigInt) => l repeat r case _ => error[Any]("operation not supported") } } 15
  17. Limitations 16

  18. Generic types Because child types must form a simple bijection

    with parent class type, it is currently impossible to synthesize constructors for polymorphic type such as Option[T]: abstract class Any1 case class Any1Option[T](x: Option[T]) extends Any1 17
  19. Structural types parametrized by Any Because ADTs cannot contain recursive

    references through non-structural types such as Set, it is currently impossible to define types such as: abstract class Any1 case class Any1SetAny(value: Set[Any1]) extends Any1 18
  20. Hack 19

  21. Mutable types id.setType(Any1Type) ￿ Current implementation mutates Identifiers type. ￿

    Only meant as a temporary hack, will be fixed soon. 20
  22. What’s left? 21

  23. Preconditions Currently not possible to write: def reverse(lst: Any): Any

    = { require(lst.isInstanceOf[List]) if (lst == Nil()) Nil() else reverse(lst.tail) ++ Cons(lst.head, Nil()) } ensuring (_.size == lst.size) Can’t handle this in translation to Z3: require(lst.isInstanceOf[Any1List]) Without those preconditions, counter-examples are yielded for postconditions. 22
  24. Match exhaustivity Implicit case classes show up as counter-examples, which

    is something we might not want. 23
  25. Other applications 24

  26. Other applications Type inference for uni-typed programs 25

  27. Other applications Type inference for uni-typed programs Port theorems from

    ACL2 to Leon 25
  28. Thank you 26