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.

8b62135e6fe874b24bc01ed7cee448c7?s=128

Romain Ruetschi

July 14, 2015
Tweet

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