Skip to content

Commit

Permalink
BigInt instead of Int, to prevent princess OOM in ensures test. Facto…
Browse files Browse the repository at this point in the history
…red out Forall and Ensures Vk/princessbigint (epfl-lara#1569)

* BigInt instead of Int, to prevent princess OOM in ensures test

* Factored ensures and Forall out of stainless.lang

* Removed unnecessary own-package-import

* Update libfiles.txt

---------

Co-authored-by: Mario Bucev <[email protected]>
  • Loading branch information
vkuncak and mario-bucev authored Aug 26, 2024
1 parent 21d0974 commit 4c65b07
Show file tree
Hide file tree
Showing 5 changed files with 123 additions and 111 deletions.
13 changes: 6 additions & 7 deletions frontends/benchmarks/verification/valid/TestEnsuresForall.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import stainless.annotation.*
import stainless.lang.{ghost => ghostExpr, *}
import stainless.lang.StaticChecks.*
import stainless.lang.Ensures.*
import stainless.lang.Forall.*
object TestEnsuresForall {

// Opaque Forall test
Expand All @@ -18,12 +20,12 @@ object TestEnsuresForall {

// ensures test

def increasing(x: Int, res: Int): Boolean = {
def increasing(x: BigInt, res: BigInt): Boolean = {
! (x >= 0) || (res >= x)
}

@opaque
def twice(f: Int => Int, x: Int): Int = {
def twice(f: BigInt => BigInt, x: BigInt): BigInt = {
require(0 <= x && ensures(f, increasing))
ghostExpr {
ensuresOf(f, increasing)(x)
Expand All @@ -32,10 +34,7 @@ object TestEnsuresForall {
f(f(x))
}.ensuring(res => res >= x)

def inc(x: Int): Int = {
if x < Int.MaxValue then x + 1
else x
}
def inc(x: BigInt): BigInt = x + 1

// externally establish given property of a function
@ghost @opaque
Expand All @@ -45,7 +44,7 @@ object TestEnsuresForall {

// now we can call twice, ensures precondition is not unfolded

def testInc(x: Int): Int = {
def testInc(x: BigInt): BigInt = {
require(100 <= x)
ghostExpr(incIncreasing)
twice(inc, x)
Expand Down
67 changes: 67 additions & 0 deletions frontends/library/stainless/lang/Ensures.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package stainless.lang
import stainless.annotation.*

object Ensures {
/* `ensures` is an `ensuring` property for first-class functions. */

// For performance, we hide forall with @opaque
@ghost @opaque @library
def ensures[A,B](f: A => B, post: (A, B) => Boolean): Boolean = {
forall[A]((a: A) => post(a, f(a)))
}

// We instantiate it explicitly with `ensuresOf` on a given argument
@ghost @opaque @library
def ensuresOf[A,B](f: A => B, post: (A, B) => Boolean)(a: A): Unit = {
require(ensures(f, post))
unfold(ensures(f,post))
}.ensuring(_ => post(a, f(a)))

/* To establish ensures(f,post), create a function with this as postcondition and unfold
the ensures, e.g.
@ghost @opaque
def incIncreasing: Unit = {
unfold(ensures(inc, increasing))
}.ensuring(_ => ensures(inc, increasing))
*/

// Larger arities

@ghost @opaque @library
def ensures2[A1,A2,B](f: (A1,A2) => B, post: (A1, A2, B) => Boolean): Boolean = {
forall[A1,A2]((a1: A1, a2: A2) => post(a1, a2, f(a1,a2)))
}
@ghost @opaque @library
def ensures2of[A1,A2,B](f: (A1,A2) => B, post: (A1, A2, B) => Boolean)(a1: A1, a2: A2): Unit = {
require(ensures2[A1,A2,B](f, post))
unfold(ensures2[A1,A2,B](f,post))
}.ensuring(_ => post(a1, a2, f(a1,a2)))

@ghost @opaque @library
def ensures3[A1,A2,A3,B](f: (A1,A2,A3) => B,
post: (A1, A2, A3, B) => Boolean): Boolean = {
forall[A1,A2,A3]((a1: A1, a2: A2, a3: A3) => post(a1, a2, a3, f(a1,a2,a3)))
}
@ghost @opaque @library
def ensures3of[A1,A2,A3,B](f: (A1,A2,A3) => B, post: (A1, A2, A3, B) => Boolean)
(a1: A1, a2: A2, a3: A3): Unit = {
require(ensures3(f, post))
unfold(ensures3(f,post))
}.ensuring(_ => post(a1, a2, a3, f(a1,a2,a3)))

@ghost @opaque @library
def ensures4[A1,A2,A3,A4,B](f: (A1,A2,A3,A4) => B,
post: (A1, A2, A3, A4, B) => Boolean): Boolean = {
forall[A1,A2,A3,A4]((a1: A1, a2: A2, a3: A3, a4: A4) => post(a1, a2, a3, a4, f(a1,a2,a3,a4)))
}
@ghost @opaque @library
def ensures4of[A1,A2,A3,A4,B](f: (A1,A2,A3,A4) => B,
post: (A1, A2, A3, A4, B) => Boolean)
(a1: A1, a2: A2, a3: A3, a4: A4): Unit = {
require(ensures4(f, post))
unfold(ensures4(f,post))
}.ensuring(_ => post(a1, a2, a3, a4, f(a1,a2,a3,a4)))

}
45 changes: 45 additions & 0 deletions frontends/library/stainless/lang/Forall.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package stainless.lang
import stainless.annotation.*
object Forall {
// Forall is opaque forall with (numbers in name instead of overloading)
@ghost @opaque @library
def Forall[A](p: A => Boolean): Boolean = forall(p)
@ghost @opaque @library
def Forall2[A,B](p: (A,B) => Boolean): Boolean = forall(p)
@ghost @opaque @library
def Forall3[A,B,C](p: (A,B,C) => Boolean): Boolean = forall(p)
@ghost @opaque @library
def Forall4[A,B,C,D](p: (A,B,C,D) => Boolean): Boolean = forall(p)
@ghost @opaque @library
def Forall5[A,B,C,D,E](p: (A,B,C,D,E) => Boolean): Boolean = forall(p)

// We instantiate it explicitly.
@ghost @opaque @library
def ForallOf[A](p: A => Boolean)(a: A): Unit = {
require(Forall(p))
unfold(Forall(p))
}.ensuring(_ => p(a))

// Predicates of larger arity
@ghost @opaque @library
def Forall2of[A,B](p: (A,B) => Boolean)(a: A, b: B): Unit = {
require(Forall2(p))
unfold(Forall2(p))
}.ensuring(_ => p(a,b))
@ghost @opaque @library
def Forall3of[A,B,C](p: (A,B,C) => Boolean)(a: A, b: B, c: C): Unit = {
require(Forall3(p))
unfold(Forall3(p))
}.ensuring(_ => p(a,b,c))
@ghost @opaque @library
def Forall4of[A,B,C,D](p: (A,B,C,D) => Boolean)(a: A, b: B, c: C, d: D): Unit = {
require(Forall4(p))
unfold(Forall4(p))
}.ensuring(_ => p(a,b,c,d))
@ghost @opaque @library
def Forall5of[A,B,C,D,E](p: (A,B,C,D,E) => Boolean)(a: A, b: B, c: C, d: D, e: E): Unit = {
require(Forall5(p))
unfold(Forall5(p))
}.ensuring(_ => p(a,b,c,d,e))

}
107 changes: 3 additions & 104 deletions frontends/library/stainless/lang/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ package object lang {
@partialEval
def partialEval[A](x: A): A = x

// TODO: put into a separate library
// TODO: put into a separate object
@ignore
implicit class Passes[A, B](io: (A, B)) {
val (in, out) = io
Expand Down Expand Up @@ -212,6 +212,8 @@ package object lang {
c1.v = t
}

// This --full-imperative stuff should perhaps move to a separate object.

@extern @library @mutable @anyHeapRef
trait AnyHeapRef {
@refEq
Expand Down Expand Up @@ -252,107 +254,4 @@ package object lang {
/* objs.mapMerge(h1, h0) == h0 */ ???
}

/* `ensures` is an `ensuring` property for first-class functions. */

// For performance, we hide forall with @opaque
@ghost @opaque @library
def ensures[A,B](f: A => B, post: (A, B) => Boolean): Boolean = {
forall[A]((a: A) => post(a, f(a)))
}

// We instantiate it explicitly with `ensuresOf` on a given argument
@ghost @opaque @library
def ensuresOf[A,B](f: A => B, post: (A, B) => Boolean)(a: A): Unit = {
require(ensures(f, post))
unfold(ensures(f,post))
}.ensuring(_ => post(a, f(a)))

/* To establish ensures(f,post), create a function with this as postcondition and unfold
the ensures, e.g.
@ghost @opaque
def incIncreasing: Unit = {
unfold(ensures(inc, increasing))
}.ensuring(_ => ensures(inc, increasing))
*/

// Larger arities

@ghost @opaque @library
def ensures2[A1,A2,B](f: (A1,A2) => B, post: (A1, A2, B) => Boolean): Boolean = {
forall[A1,A2]((a1: A1, a2: A2) => post(a1, a2, f(a1,a2)))
}
@ghost @opaque @library
def ensures2of[A1,A2,B](f: (A1,A2) => B, post: (A1, A2, B) => Boolean)(a1: A1, a2: A2): Unit = {
require(ensures2[A1,A2,B](f, post))
unfold(ensures2[A1,A2,B](f,post))
}.ensuring(_ => post(a1, a2, f(a1,a2)))

@ghost @opaque @library
def ensures3[A1,A2,A3,B](f: (A1,A2,A3) => B,
post: (A1, A2, A3, B) => Boolean): Boolean = {
forall[A1,A2,A3]((a1: A1, a2: A2, a3: A3) => post(a1, a2, a3, f(a1,a2,a3)))
}
@ghost @opaque @library
def ensures3of[A1,A2,A3,B](f: (A1,A2,A3) => B, post: (A1, A2, A3, B) => Boolean)
(a1: A1, a2: A2, a3: A3): Unit = {
require(ensures3(f, post))
unfold(ensures3(f,post))
}.ensuring(_ => post(a1, a2, a3, f(a1,a2,a3)))

@ghost @opaque @library
def ensures4[A1,A2,A3,A4,B](f: (A1,A2,A3,A4) => B,
post: (A1, A2, A3, A4, B) => Boolean): Boolean = {
forall[A1,A2,A3,A4]((a1: A1, a2: A2, a3: A3, a4: A4) => post(a1, a2, a3, a4, f(a1,a2,a3,a4)))
}
@ghost @opaque @library
def ensures4of[A1,A2,A3,A4,B](f: (A1,A2,A3,A4) => B,
post: (A1, A2, A3, A4, B) => Boolean)
(a1: A1, a2: A2, a3: A3, a4: A4): Unit = {
require(ensures4(f, post))
unfold(ensures4(f,post))
}.ensuring(_ => post(a1, a2, a3, a4, f(a1,a2,a3,a4)))

// Forall is opaque forall with (numbers in name instead of overloading)
@ghost @opaque @library
def Forall[A](p: A => Boolean): Boolean = forall(p)
@ghost @opaque @library
def Forall2[A,B](p: (A,B) => Boolean): Boolean = forall(p)
@ghost @opaque @library
def Forall3[A,B,C](p: (A,B,C) => Boolean): Boolean = forall(p)
@ghost @opaque @library
def Forall4[A,B,C,D](p: (A,B,C,D) => Boolean): Boolean = forall(p)
@ghost @opaque @library
def Forall5[A,B,C,D,E](p: (A,B,C,D,E) => Boolean): Boolean = forall(p)

// We instantiate it explicitly.
@ghost @opaque @library
def ForallOf[A](p: A => Boolean)(a: A): Unit = {
require(Forall(p))
unfold(Forall(p))
}.ensuring(_ => p(a))

// Predicates of larger arity
@ghost @opaque @library
def Forall2of[A,B](p: (A,B) => Boolean)(a: A, b: B): Unit = {
require(Forall2(p))
unfold(Forall2(p))
}.ensuring(_ => p(a,b))
@ghost @opaque @library
def Forall3of[A,B,C](p: (A,B,C) => Boolean)(a: A, b: B, c: C): Unit = {
require(Forall3(p))
unfold(Forall3(p))
}.ensuring(_ => p(a,b,c))
@ghost @opaque @library
def Forall4of[A,B,C,D](p: (A,B,C,D) => Boolean)(a: A, b: B, c: C, d: D): Unit = {
require(Forall4(p))
unfold(Forall4(p))
}.ensuring(_ => p(a,b,c,d))
@ghost @opaque @library
def Forall5of[A,B,C,D,E](p: (A,B,C,D,E) => Boolean)(a: A, b: B, c: C, d: D, e: E): Unit = {
require(Forall5(p))
unfold(Forall5(p))
}.ensuring(_ => p(a,b,c,d,e))

}
2 changes: 2 additions & 0 deletions libfiles.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ stainless/lang/PartialFunction.scala
stainless/lang/StaticChecks.scala
stainless/lang/Real.scala
stainless/lang/Either.scala
stainless/lang/Ensures.scala
stainless/lang/Forall.scala
stainless/lang/Set.scala
stainless/lang/MutableMap.scala
stainless/lang/Rational.scala
Expand Down

0 comments on commit 4c65b07

Please sign in to comment.