Skip to content

Skip Termination Checks for Imported (Pure) Functions & Methods#546

Merged
ArquintL merged 8 commits intomasterfrom
skip-termination-checks-for-imported-members
Oct 26, 2022
Merged

Skip Termination Checks for Imported (Pure) Functions & Methods#546
ArquintL merged 8 commits intomasterfrom
skip-termination-checks-for-imported-members

Conversation

@ArquintL
Copy link
Member

The parser throws away the body of non-pure functions & methods. This correctly results in skipping the proof obligations as we assume that imported packages have been verified and thus we blindly trust their specification.
However, in the case of pure functions & methods, the body is retained. This PR transforms termination measures for imported members such that Viper skips creating proof obligations for checking termination.

@ArquintL ArquintL requested a review from jcp19 October 11, 2022 08:14
@ArquintL
Copy link
Member Author

I should maybe add that changing the termination measures should not be problematic because mutual recursion is impossible due to the acyclic nature of imports and therefore the call graph analysis performed by Viper does not result in wrong results

Comment on lines +1854 to +1861
// when parsing imported packages (`specOnly` will be true), we trust the annotations of imported members
// therefore, e.g. bodies of non-pure functions & methods will be ignored. However, the body of pure
// functions & methods is retained. To avoid that Viper will generate proof obligations for checking termination
// of imported pure functions & methods, we simply translate tuple termination measures into wildcard measures (for
// a given condition). Therefore, no proof obligations will be created for checking termination of imported members.
// This is technically not necessary for imported non-pure functions & methods because they are abstract
// anyway but we apply the same transformation for simplicity as we do not have to perform a case distinction on
// whether the termination measure is part of a pure or non-pure member.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder what happens if you import an interface whose methods have a termination measure. Do we change their termination measures to a wildcard then? If so, this sounds unsound to me, as one can still implement that interface in the importing package

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very good point and you're absolutely right!
I've now adapted the approach to only adapt termination measures for method and function declarations. Since detecting this context within the parser seemed too hacky to me, I've added another parse tree postprocessor

Copy link
Contributor

@jcp19 jcp19 Oct 11, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems to me that adapting the termination measure can improve performance but we still need to prove that the body satisfies the postcondition, which is probably the part that takes the longest. I am wondering if instead of rewriting the termination measure, we can have some transformation like:

pure
requires P
ensures Q
decreases X
func f(...) (res T) {
    return body
}

==>

pure
requires P
ensures Q
ensures res === body // new postcondition
decreases X
func f(...) (res T)

This way, we do not have to reverify the body and we do not need to conditionally rewrite the termination measure.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have implemented your suggestion. However, I'm still turning decreases tuples into wildcards because Viper also checks whether postconditions terminate

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would be carefull with that rewrite. If the function is recursive, then body would contain a call to itself which I vaguely remember to be unsound in Viper right now. Maybe ask Marco, Gaurav, or Thibault there.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(As you might saw) I asked people, and I misremembered the problem: The problem are matching loops. For instance, the following program verifies:

function bar(x: Int): Int 
  ensures result == (x > 0 ? x + bar(x-1) : 0)

method test() {
  assert bar(98) == 4851
}

Therefore, this part of the transformation should be removed.

Copy link
Contributor

@Felalolf Felalolf Oct 17, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI there is some global bound on the unrollings and 98 is the most that is possible in the example. The following programs does not verify because of that (which makes a nice riddle):

function bar1(x: Int): Int 
  ensures result == (x > 0 ? x + bar1(x-1) : 0)

function bar2(x: Int): Int 
  ensures result == (x > 0 ? x + bar2(x-1) : 0)

method test() {
  assert bar1(98) == 4851
  assert bar2(1) == 1
}

@ArquintL ArquintL requested a review from jcp19 October 11, 2022 13:04
@ArquintL ArquintL marked this pull request as draft October 11, 2022 15:28
@ArquintL ArquintL marked this pull request as ready for review October 12, 2022 07:27
@ArquintL
Copy link
Member Author

I have to correct my statement from yesterday: The reduction in Viper code size is minimal and I do not see a difference in verification time unfortunately. @jcp19 you were right that the termination proofs I was seeing are coming from the implementation proofs. I'll have look at that next but in a separate branch

Copy link
Contributor

@jcp19 jcp19 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good; I wonder if we could easily introduce unit tests for the termination postprocessor

Copy link
Contributor

@Felalolf Felalolf left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In a comment in an existing thread I wrote that there might be a problem with the postcondition tranformation.

Comment on lines +310 to +319
def getBodyExpr(body: Option[(PBodyParameterInfo, PBlock)]): Option[PExpression] = body match {
case Some((_, block)) =>
block.nonEmptyStmts match {
case Vector(PReturn(Vector(expr))) => Some(expr)
case _ =>
failedNodes = failedNodes ++ createError(block, s"the imported pure function's or method's body is expected to contain only a return statement")
None
}
case None => None
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here and for getOutParam we duplicate checks, which add a bit of difficulty for maintenance as both these checks have to synced and might weaken in the future. I propose to add a comment to the other versions of these checks referencing the methods in this file, so that we do not forget that they are linked.

@ArquintL
Copy link
Member Author

@Felalolf Thanks for your inputs! I've now removed the transformation of bodies such that only termination tuples are turned into wildcard termination measures (to avoid termination proof obligations) while keeping the rest of pure functions & pure methods as is

@ArquintL
Copy link
Member Author

This PR is currently blocked by Silver PR #618 extending the termination plugin to fix the CI errors

@ArquintL ArquintL force-pushed the skip-termination-checks-for-imported-members branch from 5fd6eb4 to 8915a65 Compare October 24, 2022 14:57
@ArquintL
Copy link
Member Author

viperserver is updated to include the changes from Silver PR #618 (there are no other changes to Silver/Silicon/Carbon)

@jcp19
Copy link
Contributor

jcp19 commented Oct 26, 2022

is this ready to be merged then? @ArquintL

@ArquintL
Copy link
Member Author

Yes

@ArquintL ArquintL merged commit dbfb185 into master Oct 26, 2022
@ArquintL ArquintL deleted the skip-termination-checks-for-imported-members branch October 26, 2022 09:45
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

Successfully merging this pull request may close these issues.

3 participants