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

Fix computation of targets in let expressions #1123

Merged
merged 6 commits into from
Jul 29, 2021

Conversation

jad-hamza
Copy link
Contributor

The example LetTargets is failing in the current master branch, because the computation of targets in let expressions duplicates the path (once for the bound expression e, and once for the body b). We get targets which are not well-formed.

Copy link
Member

@samarion samarion left a 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?

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))
Copy link
Member

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?

Copy link
Contributor Author

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?

Copy link
Member

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?

Copy link
Contributor Author

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):

// 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)

Copy link
Member

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)?

Copy link
Contributor Author

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.

Copy link
Contributor Author

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.

Copy link
Member

@samarion samarion Jul 29, 2021

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.

Copy link
Contributor Author

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.

Copy link
Contributor

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

@jad-hamza
Copy link
Contributor Author

I'm not sure I understand, is the function inlining change related to the target computation issue?

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

@jad-hamza jad-hamza merged commit 078348e into epfl-lara:master Jul 29, 2021
@jad-hamza jad-hamza deleted the let-targets branch July 29, 2021 12:58
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