Skip to content

Fix side-effect check on loop invariants #5942

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

Merged
merged 2 commits into from
May 14, 2021

Conversation

SaswatPadhi
Copy link
Contributor

@SaswatPadhi SaswatPadhi commented Mar 16, 2021

Fixes #5915.

Invariant predicates were being cleaned (C -> logic via clean_expr) within convert_loop_invariant and were rejected if any intermediate goto instructions were introduced during the cleaning process. However, this check is overly conservative -- it ends up rejecting some complex, but pure, Boolean expressions that are compiled to multiple goto instructions.

We replace this logic with a call to has_subexpr with ID_side_effect.

  • Each commit message has a non-empty body, explaining why the change was made.
  • Methods or procedures I have added are documented, following the guidelines provided in CODING_STANDARD.md.
  • N/A The feature or user visible behaviour I have added or modified has been documented in the User Guide in doc/cprover-manual/
  • Regression or unit tests are included, or existing tests cover the modified code (in this case I have detailed which ones those are in the commit message).
  • N/A My commit message includes data points confirming performance improvements (if claimed).
  • My PR is restricted to a single feature or bugfix.
  • N/A White-space or formatting changes outside the feature-related changed lines are in commits of their own.

@SaswatPadhi SaswatPadhi changed the title Side effects fix Bug fix for false positives from side-effect checks on loop invariants Mar 16, 2021
@feliperodri feliperodri added aws Bugs or features of importance to AWS CBMC users bugfix Code Contracts Function and loop contracts labels Mar 16, 2021
@codecov
Copy link

codecov bot commented Mar 16, 2021

Codecov Report

Merging #5942 (6a3414d) into develop (dd36155) will decrease coverage by 0.23%.
The diff coverage is 93.27%.

❗ Current head 6a3414d differs from pull request most recent head 1debdc7. Consider uploading reports for the commit 1debdc7 to get more accurate results
Impacted file tree graph

@@             Coverage Diff             @@
##           develop    #5942      +/-   ##
===========================================
- Coverage    75.33%   75.09%   -0.24%     
===========================================
  Files         1447     1447              
  Lines       157988   158066      +78     
===========================================
- Hits        119013   118704     -309     
- Misses       38975    39362     +387     
Impacted Files Coverage Δ
src/ansi-c/c_typecheck_base.h 100.00% <ø> (+13.33%) ⬆️
src/goto-checker/symex_coverage.cpp 95.76% <ø> (ø)
src/goto-programs/goto_convert_class.h 87.30% <ø> (ø)
src/goto-programs/goto_convert.cpp 91.69% <33.33%> (-0.24%) ⬇️
src/ansi-c/c_typecheck_expr.cpp 74.89% <81.25%> (+13.49%) ⬆️
src/goto-instrument/code_contracts.cpp 85.34% <96.62%> (+0.60%) ⬆️
src/ansi-c/c_expr.h 100.00% <100.00%> (ø)
src/ansi-c/c_typecheck_base.cpp 78.29% <100.00%> (+22.37%) ⬆️
src/goto-instrument/code_contracts.h 93.75% <100.00%> (-0.37%) ⬇️
src/goto-programs/link_goto_model.cpp 0.00% <0.00%> (-77.97%) ⬇️
... and 52 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 0b4a3e8...1debdc7. Read the comment docs.

@SaswatPadhi SaswatPadhi force-pushed the side_effects_fix branch 3 times, most recently from 3f1a56a to d1532ea Compare March 25, 2021 00:55
// at multiple code points around a loop.
// Rather than performing `clean_expr` during compilation,
// we convert the invariant to logic at these various code points
// only when they are enforced (using goto-instrument).
Copy link
Collaborator

Choose a reason for hiding this comment

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

So... this means that the output of goto_convert will no longer be side effect free? So I can now write code that has a different meaning if run with and without code contracts? That doesn't seem like a very safe design.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Without having reviewed the details just yet: the short term idea is to move all invariant-related processing (as far as possible) to code_contracts.cpp, knowing that this will leave annotations in there that wouldn't satisfy side-effect freedom. Once this has been cleaned up (right now it's a mess with parts of the processing happening in all sorts of places), it should be fully integrated into goto-program conversion, possibly leaving little or (ideally) nothing left in goto-instrument.

@SaswatPadhi
Copy link
Contributor Author

@martin-cs @tautschnig

After a lot of back and forth discussion and changes, it seemed like at least for the invariants that we currently write, it's sufficient to just check for ID_side_effect subexpressions. I left a TODO to revisit the has_sideeffect function later and allow calling functions that have an empty ASSIGNS clause from within loop invariants. If you think we might also need to handle some other cases, please suggest -- I'd be happy to include them as part of this change, or leave a TODO to revisit later (if it gets too complicated).

@SaswatPadhi SaswatPadhi changed the title Bug fix for false positives from side-effect checks on loop invariants Fix side-effect check on loop invariants May 12, 2021
@SaswatPadhi SaswatPadhi force-pushed the side_effects_fix branch 2 times, most recently from 7e1f9c1 to f4b312a Compare May 12, 2021 22:03
@@ -26,6 +28,8 @@ Date: February 2016
#include <util/namespace.h>
#include <util/pointer_expr.h>

#include "loop_utils.h"
Copy link
Collaborator

Choose a reason for hiding this comment

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

Wouldn't forward declarations do the trick?

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, resolved this comment by mistake without addressing it.

I #included this header file for the loopt type, which is typedefed as:

typedef const natural_loops_mutablet::natural_loopt loopt;

Could I forward declare it here somehow? I wasn't sure how to.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I believe one of typename loopt; or class loopt; might still work, and likely the compiler will telly you if it doesn't :-) Not high priority, though.

We can maintain a `protected` `goto_convertt` field in our class, which would be reusable, rather than trying to instantiate an object for each codet -> goto_programt transformation.
Loop invariants were rejected during compilation if any intermediate goto
instructions were introduced during the cleaning process.
However, this check is overly conservative -- it ends up rejecting some complex,
but pure, Boolean expressions that are compiled to multiple goto instructions.

We replace this logic with a call to `has_subexpr` with `ID_side_effect`.
Copy link
Collaborator

@martin-cs martin-cs left a comment

Choose a reason for hiding this comment

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

My initial reason for objection, that we were changing a major post condition of goto-convert is gone so there is no reason for me to block this.

Some thoughts:
*. Are similar conditions checked for pre/post?
*. Are the restrictions documented in the manual?

Longer-term, do we want to ban function calls from these annotations? One thing I found nice about SPARK was being able to define predicates (like is_initialised and is_well_structured) using Ada functions. If there isn't a way of doing this you risk a lot of parallel maintenance when altering common invariants. I guess macros would be one option but these don't work for things like is_sorted_tree and having options of how much to expand them. This is well beyond the scope of this PR but is something worth thinking about IMHO and will touch this code.

no_sideeffects.instructions.empty(),
"loop invariant is not side-effect free",
code.find_source_location());
if(has_subexpr(invariant, ID_side_effect))
Copy link
Collaborator

Choose a reason for hiding this comment

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

As I recall, this was the really contentious part last time around. This seems much less disruptive.

I think throwing the exception is correct because the user can trigger this even if the tool is non-buggy. I wonder if it would be more user-friendly to check this earlier but ... I guess we need to check it here as well.

I wonder if this might still be a bit too conservative but, if it works for you then fine.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thank you, yes earlier I had removed this part entirely and was doing some checking in goto-instrument instead. Now this check is performed at goto-cc compilation.

I wonder if this might still be a bit too conservative but, if it works for you then fine.

I completely agree with you. And it's on our roadmap to allow function calls within contracts, if those functions have an empty assigns clause (so no side effects).

@SaswatPadhi
Copy link
Contributor Author

Thank you for taking a look, @martin-cs.

I just wanted to mention that @feliperodri and I are actively working on the issues that you pointed out:

Are similar conditions checked for pre/post?

Not currently, but we are fix this during our refactoring pass over the code contracts module

Are the restrictions documented in the manual?

Good call out! Nope, they are not. I would add it to our TODO list for documentation updates.

Longer-term, do we want to ban function calls from these annotations?

It's on our roadmap to allow function calls within contracts, if those functions have an empty assigns clause (so no side effects).

@SaswatPadhi SaswatPadhi merged commit 0ea7f13 into diffblue:develop May 14, 2021
@SaswatPadhi SaswatPadhi deleted the side_effects_fix branch May 14, 2021 16:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
aws Bugs or features of importance to AWS CBMC users bugfix Code Contracts Function and loop contracts
Projects
None yet
Development

Successfully merging this pull request may close these issues.

False positive from side-effect check in loop contracts
4 participants