$30 off During Our Annual Pro Sale. View Details »

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

    View Slide

  2. Motivation
    1

    View Slide

  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

    View Slide

  4. Implementation
    3

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  17. Limitations
    16

    View Slide

  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

    View Slide

  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

    View Slide

  20. Hack
    19

    View Slide

  21. Mutable types
    id.setType(Any1Type)
    Current implementation mutates Identifiers type.
    Only meant as a temporary hack, will be fixed soon.
    20

    View Slide

  22. What’s left?
    21

    View Slide

  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

    View Slide

  24. Match exhaustivity
    Implicit case classes show up as counter-examples, which is
    something we might not want.
    23

    View Slide

  25. Other applications
    24

    View Slide

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

    View Slide

  27. Other applications
    Type inference for uni-typed programs
    Port theorems from ACL2 to Leon
    25

    View Slide

  28. Thank you
    26

    View Slide