-
Notifications
You must be signed in to change notification settings - Fork 55
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
Fix computation of targets in let expressions #1123
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure I understand, is the function inlining change related to the target computation issue?
core/src/main/scala/stainless/extraction/inlining/FunctionInlining.scala
Outdated
Show resolved
Hide resolved
exprOps.freshenLocals(tfd.fullBody) | ||
// simple path for inlining when all arguments are values, and the function's body doesn't contain other function invocations | ||
if (specced.specs.isEmpty && args.forall(isValue) && !exprOps.exists { _.isInstanceOf[FunctionInvocation] }(tfd.fullBody)) { | ||
exprOps.replaceFromSymbols(tfd.params.zip(args).toMap, exprOps.freshenLocals(tfd.fullBody)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doesn't this mean we'll be duplicating potential checks in the function's body?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes it can happen. Maybe we should let the user decide when to avoid let-bindings?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you just wrap the full body in @unchecked
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This comment suggests that if we do that without let-binding, the @unchecked
annotation could leak to not checking postconditions (I don't remember why):
stainless/core/src/main/scala/stainless/extraction/inlining/FunctionInlining.scala
Lines 122 to 124 in 2009c76
// We bind the inlined expression in a let to avoid propagating | |
// the @unchecked annotation to postconditions, etc. | |
val newBody = let(res, inlined, res.toVariable.setPos(fi)).setPos(fi) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But this special case is guarded by specs being empty (so no postcondition)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The comment might be obsolete, I've tried to remove it and it worked on a small example (i.e. the postcondition of the caller of an inlined function wasn't ignored), let's see what happens on the full test suite.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I found an example where we should avoid the unchecked annotation except in a let binding:
object InliningUnchecked2 {
@inline def nonNegative(x: Int): Boolean = x >= 0
def test: Int = {
-10
}.ensuring(nonNegative _)
}
In postconditions, the unchecked annotation means we don't check the conjunct. I think we already had this discussion once, we're basically using unchecked annotation to mean slightly different things depending on placement.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ahhh, I understand. Wow, that's a bit dangerous. It means that if we inline lets before going to type checking, we'll stop checking post-conditions containing inlined calls?
Sounds like we should split @unchecked
into two different annotations.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(sorry I edited your post by mistake)
Ahhh, I understand. Wow, that's a bit dangerous. It means that if we inline lets before going to type checking, we'll stop checking post-conditions containing inlined calls?
Yes for instance this example (InliningUnchecked2
) would go through.
Sounds like we should split
@unchecked
into two different annotations.
Yes good idea I'll do the split in another PR. Can we give a (more or less) formal definition to the two annotations?
unchecked
: don't generate VCs when type-checking this expression (that's a bit ambiguous)dropConjunct
: drop this conjunct in VCs
Thanks for the review! I'll merge this PR for now and address this issue (and potentially others) in another one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some extra considerations to take into account when reshuffling the Unchecked
annotation: #1136
core/src/main/scala/stainless/extraction/inlining/FunctionInlining.scala
Outdated
Show resolved
Hide resolved
No sorry, I've added a bunch of unrelated commits that you can ignore, only "Fix computation of targets in let expressions" is related to that |
The example
LetTargets
is failing in the current master branch, because the computation of targets in let expressions duplicates thepath
(once for the bound expressione
, and once for the bodyb
). We get targets which are not well-formed.