Working With Existing Code

While the subset of Scala (namely, PureScala) that is currently supported by Stainless is already expressive enough to implement a lot of different data structures and algorithms, it is often the case that one would like to avoid re-implementing a piece of code from scratch in this fragment, but rather re-use some existing code, whether it is part of the codebase or pulled in from a Java or Scala library.

A wrapper for TrieMap

As a running example, let’s write a wrapper for the scala.collection.concurrent.TrieMap class. A first attempt to wrap it in a regular Stainless datatype could look like the following:

import stainless.lang._
import stainless.annotation._

import scala.collection.concurrent.TrieMap

case class TrieMapWrapper[K, V](theMap: TrieMap[K, V])

Unfortunately, this will not work as Stainless will complain that it does not know about the TrieMap type. In order to work around this, one can annotate the field with the @extern annotation, which tells Stainless that the type should be treated as opaque.

import stainless.lang._
import stainless.annotation._

import scala.collection.concurrent.TrieMap
import scala.collection.concurrent.TrieMap

case class TrieMapWrapper[K, V](@extern theMap: TrieMap[K, V])

Extern methods

Let’s now define a forwarding method for the contains method of TrieMap:

import stainless.lang._
import stainless.annotation._

import scala.collection.concurrent.TrieMap
import scala.collection.concurrent.TrieMap

case class TrieMapWrapper[K, V](@extern theMap: TrieMap[K, V]) {

  def contains(k: K): Boolean = {
    theMap contains k
  }
}

Once again, this will fail because, from Stainless’ point of view, theMap has an opaque type and thus has no contains method. By annotating the method itself with @extern, Stainless will not attempt to extract the method’s body, and we can thus freely refer to any of TrieMap’s methods:

import stainless.lang._
import stainless.annotation._

import scala.collection.concurrent.TrieMap

case class TrieMapWrapper[K, V](@extern theMap: TrieMap[K, V]) {

  @extern
  def contains(k: K): Boolean = {
    theMap contains k
  }
}

Note

Methods marked @extern are allowed to mention types which Stainless is not able to extract. Such types will be replaced by the unknown type ? during the recovery phase. One can inspect which types are replaced during recovery, by supplying the --debug=recovery flag.

Contracts

Let’s also define another extern function, which creates a new empty map:

object TrieMapWrapper {
  @extern
  def empty[K, V]: TrieMapWrapper[K, V] = {
    TrieMapWrapper(TrieMap.empty[K, V])
  }
}

def prop1 = {
  val wrapper = TrieMapWrapper.empty[Int, String]
  assert(!wrapper.contains(42)) // invalid
}

Indeed, because Stainless does not know about TrieMap.empty, it cannot assume by itself that the result of TrieMapWrapper.empty does not contain any entries.

We can remedy to that by adding a postcondition to the empty function which says that, for any key k of type K, the result of TrieMapWrapper.empty does not contain the key k.

object TrieMapWrapper {
  @extern
  def empty[K, V]: TrieMapWrapper[K, V] = {
    TrieMapWrapper(TrieMap.empty[K, V])
  } ensuring { res =>
    forall((k: K) => !res.contains(k))
  }
}

The assertion above now verifies successfully.

Purity annotations

Let’s now see what happens when we call contains twice:

def prop1 = {
  val wrapper = TrieMapWrapper.empty[Int, String]
  assert(!wrapper.contains(42))
  assert(!wrapper.contains(42))
}
  ┌───────────────────┐
╔═╡ stainless summary ╞═══════════════════════════════════════════════════╗
║ └───────────────────┘                                                   ║
║ prop1  body assertion  valid    U:smt-z3  ExternField.scala:46:5  0.018 ║
║ prop1  body assertion  invalid  U:smt-z3  ExternField.scala:47:5  0.110 ║
╚═════════════════════════════════════════════════════════════════════════╝

The second assertion (perhaps surprisingly) fails to verify. This stems from the fact that Stainless assumes by default that extern functions and methods mutate their arguments. Indeed, because Stainless does not know about the body of such methods, it cannot know whether such a function is pure or not. It is thus up to the user to instruct Stainless otherwise, by annotating the function with @pure:

case class TrieMapWrapper[K, V](@extern theMap: TrieMap[K, V]) {

  @extern @pure
  def contains(k: K): Boolean = {
    theMap contains k
  }
}

With the annotation, the two assertions above now verify:

  ┌───────────────────┐
╔═╡ stainless summary ╞═════════════════════════════════════════════════╗
║ └───────────────────┘                                                 ║
║ prop1  body assertion  valid  U:smt-z3  ExternField.scala:46:5  0.018 ║
║ prop1  body assertion  valid  U:smt-z3  ExternField.scala:48:5  0.110 ║
╚═══════════════════════════════════════════════════════════════════════╝

We can now define the other methods of interest, with their appropriate contract:

import stainless.lang._
import stainless.annotation._
import scala.annotation.meta.field

import scala.collection.concurrent.TrieMap

case class TrieMapWrapper[K, V](
  @extern
  theMap: TrieMap[K, V]
) {

  @extern @pure
  def contains(k: K): Boolean = {
    theMap contains k
  }

  @extern
  def insert(k: K, v: V): Unit = {
    theMap.update(k, v)
  } ensuring {
    this.contains(k) &&
    this.apply(k) == v
  }

  @extern @pure
  def apply(k: K): V = {
    require(contains(k))
    theMap(k)
  }
}

object TrieMapWrapper {
  @extern @pure
  def empty[K, V]: TrieMapWrapper[K, V] = {
    TrieMapWrapper(TrieMap.empty[K, V])
  } ensuring { res =>
    forall((k: K) => !res.contains(k))
  }
}

And we can now reason about our wrapper for TrieMap:

def prop2 = {
  val wrapper = TrieMapWrapper.empty[BigInt, String]
  assert(!wrapper.contains(42))
  wrapper.insert(42, "Hello")
  assert(wrapper.contains(42))
  assert(wrapper(42) == "Hello")
}
  ┌───────────────────┐
╔═╡ stainless summary ╞═════════════════════════════════════════════════════════════════════════════════╗
║ └───────────────────┘                                                                                 ║
║ prop2  body assertion                                 valid  U:smt-z3  ExternField.scala:56:5   0.023 ║
║ prop2  body assertion                                 valid  U:smt-z3  ExternField.scala:58:5   0.095 ║
║ prop2  body assertion                                 valid  U:smt-z3  ExternField.scala:59:5   0.080 ║
║ prop2  precond. (apply[BigInt, String](wrapper, 42))  valid  U:smt-z3  ExternField.scala:59:12  0.200 ║
╟-------------------------------------------------------------------------------------------------------╢
║ total: 4    valid: 4    (0 from cache) invalid: 0    unknown: 0    time:   0.398                      ║
╚═══════════════════════════════════════════════════════════════════════════════════════════════════════╝