stainless icon indicating copy to clipboard operation
stainless copied to clipboard

Function equivalence with comparefuns crashes

Open jad-hamza opened this issue 2 years ago • 3 comments

Hi @drganam @vkuncak, can Stainless generate equivalence lemmas for functions when one of the function calls other functions? (here replace vs slowReplace). I get a crash on this example (from a FV student)

stainless  Test.scala --solvers=smt-z3 --timeout=10 --comparefuns=replace --models=slowReplace --batched --debug=trees --debug-phases=Trace
import stainless.collection._

object Test {

  def split[T](l: List[T], x: T): List[List[T]] = l match {
    case Nil() => List[List[T]](List[T]())
    case Cons(y, ys) if x == y =>
      Nil[T]() :: split(ys, x)
    case Cons(y, ys) =>
      val r = split(ys, x)
      (y :: r.head) :: r.tail
  }

  def join[T](ll: List[List[T]], l: List[T]): List[T] = ll match {
    case Nil() => Nil[T]()
    case Cons(l1, Nil()) => l1 // if you miss this case, Stainless finds a counter-example to theorem
    case Cons(l1, ls) => l1 ++ l ++ join(ls, l) 
  }

  def replace[T](l1: List[T], x: T, l2: List[T]): List[T] = l1 match {
    case Nil() => Nil[T]()
    case Cons(y, ys) if x == y => l2 ++ replace(ys, x, l2)
    case Cons(y, ys) => y :: replace(ys, x, l2)
  }

  def theorem[T](l1: List[T], x: T, l2: List[T]): Unit = {
    if (!l1.isEmpty) theorem(l1.tail, x, l2) // recursive proof

  }.ensuring(_ =>
    join(split(l1, x), l2) == replace(l1, x, l2)
  )

  // could be checked for equivalence with `replace` thanks to --comparefuns
  def slowReplace[T](l1: List[T], x: T, l2: List[T]): Unit = join(split(l1, x), l2)

}

stainless Test.scala --solvers=smt-z3 --timeout=10 --comparefuns=replace --models=slowReplace --batched --debug=trees

Show output
[ Debug  ] Symbols before Trace
[ Debug  ] 
[ Debug  ] -------------Functions-------------
[ Debug  ] @library
[ Debug  ] def head[T](thiss: List[T]): T = {
[ Debug  ]   require(@ghost (thiss \u2260 Nil[T]()))
[ Debug  ]   thiss match {
[ Debug  ]     case Cons(h, _) =>
[ Debug  ]       h
[ Debug  ]   }
[ Debug  ] }
[ Debug  ] 
[ Debug  ] @library
[ Debug  ] def isEmpty[T](thiss: List[T]): Boolean = thiss match {
[ Debug  ]   case Nil() =>
[ Debug  ]     true
[ Debug  ]   case _ =>
[ Debug  ]     false
[ Debug  ] }
[ Debug  ] 
[ Debug  ] def join[T](ll: List[List[T]], l: List[T]): List[T] = ll match {
[ Debug  ]   case Nil() =>
[ Debug  ]     Nil[T]()
[ Debug  ]   case Cons(l1, Nil()) =>
[ Debug  ]     l1
[ Debug  ]   case Cons(l1, ls) =>
[ Debug  ]     ++[T](++[T](l1, l), join[T](ls, l))
[ Debug  ] }
[ Debug  ] 
[ Debug  ] @library
[ Debug  ] def ++[T](thiss: List[T], that: List[T]): List[T] = {
[ Debug  ]   thiss match {
[ Debug  ]     case Nil() =>
[ Debug  ]       that
[ Debug  ]     case Cons(x, xs) =>
[ Debug  ]       Cons[T](x, ++[T](xs, that))
[ Debug  ]   }
[ Debug  ] } ensuring {
[ Debug  ]   (res: List[T]) => @ghost ((content[T](res) == content[T](thiss) \u222A content[T](that) && size[T](res) == size[T](thiss) + size[T](that)) && (that \u2260 Nil[T]() || res == thiss))
[ Debug  ] }
[ Debug  ] 
[ Debug  ] def replace[T](l1: List[T], x: T, l2: List[T]): List[T] = l1 match {
[ Debug  ]   case Nil() =>
[ Debug  ]     Nil[T]()
[ Debug  ]   case Cons(y, ys) if x == y =>
[ Debug  ]     ++[T](l2, replace[T](ys, x, l2))
[ Debug  ]   case Cons(y, ys) =>
[ Debug  ]     val rassoc$4: T = y
[ Debug  ]     ::[T](replace[T](ys, x, l2), rassoc$4)
[ Debug  ] }
[ Debug  ] 
[ Debug  ] @library
[ Debug  ] def size[T](thiss: List[T]): BigInt = {
[ Debug  ]   thiss match {
[ Debug  ]     case Nil() =>
[ Debug  ]       0
[ Debug  ]     case Cons(h, t) =>
[ Debug  ]       1 + size[T](t)
[ Debug  ]   }
[ Debug  ] } ensuring {
[ Debug  ]   (x$1: BigInt) => @ghost (x$1 >= 0)
[ Debug  ] }
[ Debug  ] 
[ Debug  ] def split[T](l: List[T], x: T): List[List[T]] = l match {
[ Debug  ]   case Nil() =>
[ Debug  ]     Cons[List[T]](Nil[T](), Nil[List[T]]())
[ Debug  ]   case Cons(y, ys) if x == y =>
[ Debug  ]     val rassoc$1: List[T] = {
[ Debug  ]       val rassoc$1: List[T] = Nil[T]()
[ Debug  ]       assert(rassoc$1 is Nil, "Inner refinement lifting")
[ Debug  ]       rassoc$1
[ Debug  ]     }
[ Debug  ]     ::[List[T]](split[T](ys, x), rassoc$1)
[ Debug  ]   case Cons(y, ys) =>
[ Debug  ]     val r: List[List[T]] = split[T](ys, x)
[ Debug  ]     val rassoc$2: T = y
[ Debug  ]     val rassoc$3: List[T] = ::[T](head[List[T]](r), rassoc$2)
[ Debug  ]     ::[List[T]](tail[List[T]](r), rassoc$3)
[ Debug  ] }
[ Debug  ] 
[ Debug  ] def slowReplace[T](l1: List[T], x: T, l2: List[T]): Unit = {
[ Debug  ]   val tmp: List[T] = join[T](split[T](l1, x), l2)
[ Debug  ]   ()
[ Debug  ] }
[ Debug  ] 
[ Debug  ] @library
[ Debug  ] def ::[T](thiss: List[T], t: T): List[T] = Cons[T](t, thiss)
[ Debug  ] 
[ Debug  ] @library
[ Debug  ] def content[T](thiss: List[T]): Set[T] = thiss match {
[ Debug  ]   case Nil() =>
[ Debug  ]     {}
[ Debug  ]   case Cons(h, t) =>
[ Debug  ]     {h} \u222A content[T](t)
[ Debug  ] }
[ Debug  ] 
[ Debug  ] @library
[ Debug  ] def tail[T](thiss: List[T]): List[T] = {
[ Debug  ]   require(@ghost (thiss \u2260 Nil[T]()))
[ Debug  ]   thiss match {
[ Debug  ]     case Cons(_, t) =>
[ Debug  ]       t
[ Debug  ]   }
[ Debug  ] }
[ Debug  ] 
[ Debug  ] def theorem[T](l1: List[T], x: T, l2: List[T]): Unit = {
[ Debug  ]   if (\u00ACisEmpty[T](l1)) {
[ Debug  ]     theorem[T](tail[T](l1), x, l2)
[ Debug  ]   } else {
[ Debug  ]     ()
[ Debug  ]   }
[ Debug  ] } ensuring {
[ Debug  ]   (x$1: Unit) => join[T](split[T](l1, x), l2) == replace[T](l1, x, l2)
[ Debug  ] }
[ Debug  ] 
[ Debug  ] -------------Sorts-------------
[ Debug  ] @library
[ Debug  ] abstract class List[T]
[ Debug  ] case class Nil[T]() extends List[T]
[ Debug  ] case class Cons[T](h: T, t: List[T]) extends List[T]
[ Debug  ] 
[ Debug  ] @synthetic
[ Debug  ] abstract class Object
[ Debug  ] case class Open(value: BigInt) extends Object
[ Debug  ] 
[ Debug  ] 
[ Debug  ] 
[ Debug  ] 
[ Debug  ] Symbols after Trace
[ Debug  ] 
[ Debug  ] -------------Functions-------------
[ Debug  ] @library
[ Debug  ] def head[T](thiss: List[T]): T = {
[ Debug  ]   require(@ghost (thiss \u2260 Nil[T]()))
[ Debug  ]   thiss match {
[ Debug  ]     case Cons(h, _) =>
[ Debug  ]       h
[ Debug  ]   }
[ Debug  ] }
[ Debug  ] 
[ Debug  ] @library
[ Debug  ] def isEmpty[T](thiss: List[T]): Boolean = thiss match {
[ Debug  ]   case Nil() =>
[ Debug  ]     true
[ Debug  ]   case _ =>
[ Debug  ]     false
[ Debug  ] }
[ Debug  ] 
[ Debug  ] def join[T](ll: List[List[T]], l: List[T]): List[T] = ll match {
[ Debug  ]   case Nil() =>
[ Debug  ]     Nil[T]()
[ Debug  ]   case Cons(l1, Nil()) =>
[ Debug  ]     l1
[ Debug  ]   case Cons(l1, ls) =>
[ Debug  ]     ++[T](++[T](l1, l), join[T](ls, l))
[ Debug  ] }
[ Debug  ] 
[ Debug  ] @library
[ Debug  ] def ++[T](thiss: List[T], that: List[T]): List[T] = {
[ Debug  ]   thiss match {
[ Debug  ]     case Nil() =>
[ Debug  ]       that
[ Debug  ]     case Cons(x, xs) =>
[ Debug  ]       Cons[T](x, ++[T](xs, that))
[ Debug  ]   }
[ Debug  ] } ensuring {
[ Debug  ]   (res: List[T]) => @ghost ((content[T](res) == content[T](thiss) \u222A content[T](that) && size[T](res) == size[T](thiss) + size[T](that)) && (that \u2260 Nil[T]() || res == thiss))
[ Debug  ] }
[ Debug  ] 
[ Debug  ] def replace[T](l1: List[T], x: T, l2: List[T]): List[T] = l1 match {
[ Debug  ]   case Nil() =>
[ Debug  ]     Nil[T]()
[ Debug  ]   case Cons(y, ys) if x == y =>
[ Debug  ]     ++[T](l2, replace[T](ys, x, l2))
[ Debug  ]   case Cons(y, ys) =>
[ Debug  ]     val rassoc$4: T = y
[ Debug  ]     ::[T](replace[T](ys, x, l2), rassoc$4)
[ Debug  ] }
[ Debug  ] 
[ Debug  ] @library
[ Debug  ] def size[T](thiss: List[T]): BigInt = {
[ Debug  ]   thiss match {
[ Debug  ]     case Nil() =>
[ Debug  ]       0
[ Debug  ]     case Cons(h, t) =>
[ Debug  ]       1 + size[T](t)
[ Debug  ]   }
[ Debug  ] } ensuring {
[ Debug  ]   (x$1: BigInt) => @ghost (x$1 >= 0)
[ Debug  ] }
[ Debug  ] 
[ Debug  ] def split[T](l: List[T], x: T): List[List[T]] = l match {
[ Debug  ]   case Nil() =>
[ Debug  ]     Cons[List[T]](Nil[T](), Nil[List[T]]())
[ Debug  ]   case Cons(y, ys) if x == y =>
[ Debug  ]     val rassoc$1: List[T] = {
[ Debug  ]       val rassoc$1: List[T] = Nil[T]()
[ Debug  ]       assert(rassoc$1 is Nil, "Inner refinement lifting")
[ Debug  ]       rassoc$1
[ Debug  ]     }
[ Debug  ]     ::[List[T]](split[T](ys, x), rassoc$1)
[ Debug  ]   case Cons(y, ys) =>
[ Debug  ]     val r: List[List[T]] = split[T](ys, x)
[ Debug  ]     val rassoc$2: T = y
[ Debug  ]     val rassoc$3: List[T] = ::[T](head[List[T]](r), rassoc$2)
[ Debug  ]     ::[List[T]](tail[List[T]](r), rassoc$3)
[ Debug  ] }
[ Debug  ] 
[ Debug  ] def slowReplace[T](l1: List[T], x: T, l2: List[T]): Unit = {
[ Debug  ]   val tmp: List[T] = join[T](split[T](l1, x), l2)
[ Debug  ]   ()
[ Debug  ] }
[ Debug  ] 
[ Debug  ] @library
[ Debug  ] def ::[T](thiss: List[T], t: T): List[T] = Cons[T](t, thiss)
[ Debug  ] 
[ Debug  ] @library
[ Debug  ] def content[T](thiss: List[T]): Set[T] = thiss match {
[ Debug  ]   case Nil() =>
[ Debug  ]     {}
[ Debug  ]   case Cons(h, t) =>
[ Debug  ]     {h} \u222A content[T](t)
[ Debug  ] }
[ Debug  ] 
[ Debug  ] @library
[ Debug  ] def tail[T](thiss: List[T]): List[T] = {
[ Debug  ]   require(@ghost (thiss \u2260 Nil[T]()))
[ Debug  ]   thiss match {
[ Debug  ]     case Cons(_, t) =>
[ Debug  ]       t
[ Debug  ]   }
[ Debug  ] }
[ Debug  ] 
[ Debug  ] def theorem[T](l1: List[T], x: T, l2: List[T]): Unit = {
[ Debug  ]   if (\u00ACisEmpty[T](l1)) {
[ Debug  ]     theorem[T](tail[T](l1), x, l2)
[ Debug  ]   } else {
[ Debug  ]     ()
[ Debug  ]   }
[ Debug  ] } ensuring {
[ Debug  ]   (x$1: Unit) => join[T](split[T](l1, x), l2) == replace[T](l1, x, l2)
[ Debug  ] }
[ Debug  ] 
[ Debug  ] @derived(Test.slowReplace$Test.replace)
[ Debug  ] @derived(slowReplace)
[ Debug  ] def Test.slowReplace$Test.replace[T](l1: List[T], x: T, l2: List[T]): Unit = {
[ Debug  ]   ()
[ Debug  ] } ensuring {
[ Debug  ]   (res: Unit) => slowReplace[T](l1, x, l2) == replace[T](l1, x, l2)
[ Debug  ] }
[ Debug  ] 
[ Debug  ] -------------Sorts-------------
[ Debug  ] @library
[ Debug  ] abstract class List[T]
[ Debug  ] case class Nil[T]() extends List[T]
[ Debug  ] case class Cons[T](h: T, t: List[T]) extends List[T]
[ Debug  ] 
[ Debug  ] @synthetic
[ Debug  ] abstract class Object
[ Debug  ] case class Open(value: BigInt) extends Object
[ Debug  ] 
[ Debug  ] 
[ Debug  ] 
[ Debug  ] 
[ Debug  ] 
[ Debug  ] Ensuring well-formedness after phase Trace...
[ Error  ] Type error: {
[ Error  ]   ()
[ Error  ] } ensuring {
[ Error  ]   (res: Unit) => slowReplace[T](l1, x, l2) == replace[T](l1, x, l2)
[ Error  ] }, expected Unit,
[ Error  ] found <untyped>
[ Error  ] 
[ Error  ] Typing explanation:
[ Error  ] {
[ Error  ]   ()
[ Error  ] } ensuring {
[ Error  ]   (res: Unit) => slowReplace[T](l1, x, l2) == replace[T](l1, x, l2)
[ Error  ] } is of type <untyped>
[ Error  ]   () is of type Unit
[ Error  ]   (res: Unit) => slowReplace[T](l1, x, l2) == replace[T](l1, x, l2) is of type <untyped>
[ Error  ]     slowReplace[T](l1, x, l2) == replace[T](l1, x, l2) is of type <untyped>
[ Error  ]       slowReplace[T](l1, x, l2) is of type <untyped>
[ Error  ]         l1 is of type List[T]
[ Error  ]         x is of type T
[ Error  ]         l2 is of type List[T] because slowReplace was instantiated with T:=T with type (List[T],T,List[T]) => Unit
[ Error  ]       replace[T](l1, x, l2) is of type <untyped>
[ Error  ]         l1 is of type List[T]
[ Error  ]         x is of type T
[ Error  ]         l2 is of type List[T] because replace was instantiated with T:=T with type (List[T],T,List[T]) => List[T]
[ Fatal  ] Well-formedness check failed after phase Trace
[ Error  ] Stainless terminated with an error.
[ Error  ] Debug output is available in the file `stainless-stack-trace.txt`. If the crash is caused by Stainless, you may report your issue on https://github.com/epfl-lara/stainless/issues
[ Error  ] You may use --debug=stack to have the stack trace displayed in the output.

jad-hamza avatar May 17 '22 14:05 jad-hamza

This works somewhat in Dragana's branch. Needs to be rebased. Dragana might appreciate help with that.

On Tue, May 17, 2022, 16:54 Jad Hamza @.***> wrote:

Hi @drganam https://github.com/drganam @vkuncak https://github.com/vkuncak, can Stainless generate equivalence lemmas for functions when one of the function calls other functions? (here replace vs slowReplace). I get a crash on this example (from a FV student)

stainless Test.scala --solvers=smt-z3 --timeout=10 --comparefuns=replace --models=slowReplace --batched --debug=trees --debug-phases=Trace

import stainless.collection._ object Test {

def split[T](l: List[T], x: T): List[List[T]] = l match { case Nil() => ListList[T] case Cons(y, ys) if x == y => NilT :: split(ys, x) case Cons(y, ys) => val r = split(ys, x) (y :: r.head) :: r.tail }

def join[T](ll: List[List[T]], l: List[T]): List[T] = ll match { case Nil() => NilT case Cons(l1, Nil()) => l1 // if you miss this case, Stainless finds a counter-example to theorem case Cons(l1, ls) => l1 ++ l ++ join(ls, l) }

def replace[T](l1: List[T], x: T, l2: List[T]): List[T] = l1 match { case Nil() => NilT case Cons(y, ys) if x == y => l2 ++ replace(ys, x, l2) case Cons(y, ys) => y :: replace(ys, x, l2) }

def theorem[T](l1: List[T], x: T, l2: List[T]): Unit = { if (!l1.isEmpty) theorem(l1.tail, x, l2) // recursive proof

}.ensuring(_ => join(split(l1, x), l2) == replace(l1, x, l2) )

// could be checked for equivalence with replace thanks to --comparefuns def slowReplace[T](l1: List[T], x: T, l2: List[T]): Unit = join(split(l1, x), l2)

}

stainless Test.scala --solvers=smt-z3 --timeout=10 --comparefuns=replace --models=slowReplace --batched --debug=trees Show output

[ Debug ] Symbols before Trace [ Debug ] [ Debug ] -------------Functions------------- [ Debug ] @library [ Debug ] def head[T](thiss: List[T]): T = { [ Debug ] @.*** (thiss \u2260 NilT)) [ Debug ] thiss match { [ Debug ] case Cons(h, ) => [ Debug ] h [ Debug ] } [ Debug ] } [ Debug ] [ Debug ] @library [ Debug ] def isEmpty[T](thiss: List[T]): Boolean = thiss match { [ Debug ] case Nil() => [ Debug ] true [ Debug ] case _ => [ Debug ] false [ Debug ] } [ Debug ] [ Debug ] def join[T](ll: List[List[T]], l: List[T]): List[T] = ll match { [ Debug ] case Nil() => [ Debug ] NilT [ Debug ] case Cons(l1, Nil()) => [ Debug ] l1 [ Debug ] case Cons(l1, ls) => [ Debug ] ++[T](++[T](l1, l), join[T](ls, l)) [ Debug ] } [ Debug ] [ Debug ] @library [ Debug ] def ++[T](thiss: List[T], that: List[T]): List[T] = { [ Debug ] thiss match { [ Debug ] case Nil() => [ Debug ] that [ Debug ] case Cons(x, xs) => [ Debug ] Cons[T](x, ++[T](xs, that)) [ Debug ] } [ Debug ] } ensuring { [ Debug ] (res: List[T]) => @ghost ((contentT == contentT \u222A contentT && sizeT == sizeT + sizeT) && (that \u2260 NilT || res == thiss)) [ Debug ] } [ Debug ] [ Debug ] def replace[T](l1: List[T], x: T, l2: List[T]): List[T] = l1 match { [ Debug ] case Nil() => [ Debug ] NilT [ Debug ] case Cons(y, ys) if x == y => [ Debug ] ++[T](l2, replace[T](ys, x, l2)) [ Debug ] case Cons(y, ys) => [ Debug ] val rassoc$4: T = y [ Debug ] ::[T](replace[T](ys, x, l2), rassoc$4) [ Debug ] } [ Debug ] [ Debug ] @library [ Debug ] def size[T](thiss: List[T]): BigInt = { [ Debug ] thiss match { [ Debug ] case Nil() => [ Debug ] 0 [ Debug ] case Cons(h, t) => [ Debug ] 1 + sizeT [ Debug ] } [ Debug ] } ensuring { [ Debug ] (x$1: BigInt) => @ghost (x$1 >= 0) [ Debug ] } [ Debug ] [ Debug ] def split[T](l: List[T], x: T): List[List[T]] = l match { [ Debug ] case Nil() => [ Debug ] Cons[List[T]](NilT, NilList[T]) [ Debug ] case Cons(y, ys) if x == y => [ Debug ] val rassoc$1: List[T] = { [ Debug ] val rassoc$1: List[T] = NilT [ Debug ] assert(rassoc$1 is Nil, "Inner refinement lifting") [ Debug ] rassoc$1 [ Debug ] } [ Debug ] ::[List[T]](split[T](ys, x), rassoc$1) [ Debug ] case Cons(y, ys) => [ Debug ] val r: List[List[T]] = split[T](ys, x) [ Debug ] val rassoc$2: T = y [ Debug ] val rassoc$3: List[T] = ::[T](headList[T], rassoc$2) [ Debug ] ::[List[T]](tailList[T], rassoc$3) [ Debug ] } [ Debug ] [ Debug ] def slowReplace[T](l1: List[T], x: T, l2: List[T]): Unit = { [ Debug ] val tmp: List[T] = join[T](split[T](l1, x), l2) [ Debug ] () [ Debug ] } [ Debug ] [ Debug ] @library [ Debug ] def ::[T](thiss: List[T], t: T): List[T] = Cons[T](t, thiss) [ Debug ] [ Debug ] @library [ Debug ] def content[T](thiss: List[T]): Set[T] = thiss match { [ Debug ] case Nil() => [ Debug ] {} [ Debug ] case Cons(h, t) => [ Debug ] {h} \u222A contentT [ Debug ] } [ Debug ] [ Debug ] @library [ Debug ] def tail[T](thiss: List[T]): List[T] = { [ Debug ] @.*** (thiss \u2260 NilT)) [ Debug ] thiss match { [ Debug ] case Cons(, t) => [ Debug ] t [ Debug ] } [ Debug ] } [ Debug ] [ Debug ] def theorem[T](l1: List[T], x: T, l2: List[T]): Unit = { [ Debug ] if (\u00ACisEmptyT) { [ Debug ] theorem[T](tailT, x, l2) [ Debug ] } else { [ Debug ] () [ Debug ] } [ Debug ] } ensuring { [ Debug ] (x$1: Unit) => join[T](split[T](l1, x), l2) == replace[T](l1, x, l2) [ Debug ] } [ Debug ] [ Debug ] -------------Sorts------------- [ Debug ] @library [ Debug ] abstract class List[T] [ Debug ] case class NilT extends List[T] [ Debug ] case class Cons[T](h: T, t: List[T]) extends List[T] [ Debug ] [ Debug ] @synthetic [ Debug ] abstract class Object [ Debug ] case class Open(value: BigInt) extends Object [ Debug ] [ Debug ] [ Debug ] [ Debug ] [ Debug ] Symbols after Trace [ Debug ] [ Debug ] -------------Functions------------- [ Debug ] @library [ Debug ] def head[T](thiss: List[T]): T = { [ Debug ] @.*** (thiss \u2260 NilT)) [ Debug ] thiss match { [ Debug ] case Cons(h, ) => [ Debug ] h [ Debug ] } [ Debug ] } [ Debug ] [ Debug ] @library [ Debug ] def isEmpty[T](thiss: List[T]): Boolean = thiss match { [ Debug ] case Nil() => [ Debug ] true [ Debug ] case _ => [ Debug ] false [ Debug ] } [ Debug ] [ Debug ] def join[T](ll: List[List[T]], l: List[T]): List[T] = ll match { [ Debug ] case Nil() => [ Debug ] NilT [ Debug ] case Cons(l1, Nil()) => [ Debug ] l1 [ Debug ] case Cons(l1, ls) => [ Debug ] ++[T](++[T](l1, l), join[T](ls, l)) [ Debug ] } [ Debug ] [ Debug ] @library [ Debug ] def ++[T](thiss: List[T], that: List[T]): List[T] = { [ Debug ] thiss match { [ Debug ] case Nil() => [ Debug ] that [ Debug ] case Cons(x, xs) => [ Debug ] Cons[T](x, ++[T](xs, that)) [ Debug ] } [ Debug ] } ensuring { [ Debug ] (res: List[T]) => @ghost ((contentT == contentT \u222A contentT && sizeT == sizeT + sizeT) && (that \u2260 NilT || res == thiss)) [ Debug ] } [ Debug ] [ Debug ] def replace[T](l1: List[T], x: T, l2: List[T]): List[T] = l1 match { [ Debug ] case Nil() => [ Debug ] NilT [ Debug ] case Cons(y, ys) if x == y => [ Debug ] ++[T](l2, replace[T](ys, x, l2)) [ Debug ] case Cons(y, ys) => [ Debug ] val rassoc$4: T = y [ Debug ] ::[T](replace[T](ys, x, l2), rassoc$4) [ Debug ] } [ Debug ] [ Debug ] @library [ Debug ] def size[T](thiss: List[T]): BigInt = { [ Debug ] thiss match { [ Debug ] case Nil() => [ Debug ] 0 [ Debug ] case Cons(h, t) => [ Debug ] 1 + sizeT [ Debug ] } [ Debug ] } ensuring { [ Debug ] (x$1: BigInt) => @ghost (x$1 >= 0) [ Debug ] } [ Debug ] [ Debug ] def split[T](l: List[T], x: T): List[List[T]] = l match { [ Debug ] case Nil() => [ Debug ] Cons[List[T]](NilT, NilList[T]) [ Debug ] case Cons(y, ys) if x == y => [ Debug ] val rassoc$1: List[T] = { [ Debug ] val rassoc$1: List[T] = NilT [ Debug ] assert(rassoc$1 is Nil, "Inner refinement lifting") [ Debug ] rassoc$1 [ Debug ] } [ Debug ] ::[List[T]](split[T](ys, x), rassoc$1) [ Debug ] case Cons(y, ys) => [ Debug ] val r: List[List[T]] = split[T](ys, x) [ Debug ] val rassoc$2: T = y [ Debug ] val rassoc$3: List[T] = ::[T](headList[T], rassoc$2) [ Debug ] ::[List[T]](tailList[T], rassoc$3) [ Debug ] } [ Debug ] [ Debug ] def slowReplace[T](l1: List[T], x: T, l2: List[T]): Unit = { [ Debug ] val tmp: List[T] = join[T](split[T](l1, x), l2) [ Debug ] () [ Debug ] } [ Debug ] [ Debug ] @library [ Debug ] def ::[T](thiss: List[T], t: T): List[T] = Cons[T](t, thiss) [ Debug ] [ Debug ] @library [ Debug ] def content[T](thiss: List[T]): Set[T] = thiss match { [ Debug ] case Nil() => [ Debug ] {} [ Debug ] case Cons(h, t) => [ Debug ] {h} \u222A contentT [ Debug ] } [ Debug ] [ Debug ] @library [ Debug ] def tail[T](thiss: List[T]): List[T] = { [ Debug ] @.*** (thiss \u2260 NilT)) [ Debug ] thiss match { [ Debug ] case Cons(, t) => [ Debug ] t [ Debug ] } [ Debug ] } [ Debug ] [ Debug ] def theorem[T](l1: List[T], x: T, l2: List[T]): Unit = { [ Debug ] if (\u00ACisEmptyT) { [ Debug ] theorem[T](tailT, x, l2) [ Debug ] } else { [ Debug ] () [ Debug ] } [ Debug ] } ensuring { [ Debug ] (x$1: Unit) => join[T](split[T](l1, x), l2) == replace[T](l1, x, l2) [ Debug ] } [ Debug ] [ Debug ] @derived(Test.slowReplace$Test.replace) [ Debug ] @derived(slowReplace) [ Debug ] def Test.slowReplace$Test.replace[T](l1: List[T], x: T, l2: List[T]): Unit = { [ Debug ] () [ Debug ] } ensuring { [ Debug ] (res: Unit) => slowReplace[T](l1, x, l2) == replace[T](l1, x, l2) [ Debug ] } [ Debug ] [ Debug ] -------------Sorts------------- [ Debug ] @library [ Debug ] abstract class List[T] [ Debug ] case class NilT extends List[T] [ Debug ] case class Cons[T](h: T, t: List[T]) extends List[T] [ Debug ] [ Debug ] @synthetic [ Debug ] abstract class Object [ Debug ] case class Open(value: BigInt) extends Object [ Debug ] [ Debug ] [ Debug ] [ Debug ] [ Debug ] [ Debug ] Ensuring well-formedness after phase Trace... [ Error ] Type error: { [ Error ] () [ Error ] } ensuring { [ Error ] (res: Unit) => slowReplace[T](l1, x, l2) == replace[T](l1, x, l2) [ Error ] }, expected Unit, [ Error ] found [ Error ] [ Error ] Typing explanation: [ Error ] { [ Error ] () [ Error ] } ensuring { [ Error ] (res: Unit) => slowReplace[T](l1, x, l2) == replace[T](l1, x, l2) [ Error ] } is of type [ Error ] () is of type Unit [ Error ] (res: Unit) => slowReplace[T](l1, x, l2) == replace[T](l1, x, l2) is of type [ Error ] slowReplace[T](l1, x, l2) == replace[T](l1, x, l2) is of type [ Error ] slowReplace[T](l1, x, l2) is of type [ Error ] l1 is of type List[T] [ Error ] x is of type T [ Error ] l2 is of type List[T] because slowReplace was instantiated with T:=T with type (List[T],T,List[T]) => Unit [ Error ] replace[T](l1, x, l2) is of type [ Error ] l1 is of type List[T] [ Error ] x is of type T [ Error ] l2 is of type List[T] because replace was instantiated with T:=T with type (List[T],T,List[T]) => List[T] [ Fatal ] Well-formedness check failed after phase Trace [ Error ] Stainless terminated with an error. [ Error ] Debug output is available in the file stainless-stack-trace.txt. If the crash is caused by Stainless, you may report your issue on https://github.com/epfl-lara/stainless/issues [ Error ] You may use --debug=stack to have the stack trace displayed in the output.

— Reply to this email directly, view it on GitHub https://github.com/epfl-lara/stainless/issues/1264, or unsubscribe https://github.com/notifications/unsubscribe-auth/AA5CDN33LQXAXNTODJSHM63VKOXKZANCNFSM5WFHFQXA . You are receiving this because you were mentioned.Message ID: @.***>

vkuncak avatar May 17 '22 15:05 vkuncak

I can help with rebasing, I've become a skilled craftsman in that regard hehe :p Edit: https://github.com/drganam/stainless/pull/1

mario-bucev avatar May 18 '22 07:05 mario-bucev

Sorry for not seeing this sooner.

Shouldn't return type of slowReplace be List[T] and not Unit?

This works for List[T], no need for function matching even :)

Looks like a very nice addition to our new fancy EC test suite!

drganam avatar Feb 23 '23 12:02 drganam