Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ensuring does not work with return #1362

Open
two-heart opened this issue Dec 14, 2022 · 1 comment
Open

ensuring does not work with return #1362

two-heart opened this issue Dec 14, 2022 · 1 comment

Comments

@two-heart
Copy link

I would expect both examples to work the same way. E.g., by stainless translating the first to the second.

  def getOne(): Int = {
    1
  }.ensuring(res => res == 1)
  def getOne(): Int = {
    return 1
  }.ensuring(res => res == 1)

However, while the first verifies, the second gives the error:

[ Error  ] SubList.scala:43:5: value ensuring is not a member of Nothing
             }.ensuring(res => res == 1)
               ^

Note this is a minimal example. I encountered the problem when trying to prove more complex imperative code, where writing return statements makes more sense than here.

@mario-bucev
Copy link
Collaborator

Hello!
The above snippet is rejected by the Scala compiler as well:

-- [E008] Not Found Error: i1362.scala:4:4 -------------------------------------
2 |  def getOne(): Int = {
3 |    return 1
4 |  }.ensuring(res => res == 1)
  |                      ^
  |                      value ensuring is not a member of Nothing
1 error found

This is due to the return being non-local, as discussed here: scala/bug#9838

A workaround would be to extract the definition of getOne() into a new function without postconditions. Note that preconditions would still need to be specified (i.e. duplicated).

In case the function is recursive, things get a bit more spicy because one usually needs the postcondition of the said function to prove things.
For example, if we have this definition:

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

// Does not compile
def summed(x: BigInt): BigInt = {
  decreases(x)
  require(x >= 0)
  if (x == 0) return x
  return x + summed(x - 1)
}.ensuring(_ == x * (x + 1) / 2)

we could extract the body of summed as follows:

def summed(x: BigInt): BigInt = {
  decreases(x)
  require(x >= 0)
  summedImpl(x)
}.ensuring(_ == x * (x + 1) / 2)

@inlineOnce // this tells Stainless to inline this function for verification
def summedImpl(x: BigInt): BigInt = {
  require(x >= 0) // duplicated precondition
  if (x == 0) return x
  return x + summed(x - 1) // calls the original function
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants