Skip to content

Add an --assert-ensures check #4976

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

Open
wants to merge 3 commits into
base: develop
Choose a base branch
from

Conversation

angelhof
Copy link
Contributor

@angelhof angelhof commented Aug 1, 2019

Add an --assert-ensures check that asserts the ensures part of a function's contract. Many function names can be given as arguments to the check.

  • 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.
  • 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).
  • My commit message includes data points confirming performance improvements (if claimed).
  • My PR is restricted to a single feature or bugfix.
  • White-space or formatting changes outside the feature-related changed lines are in commits of their own.

Copy link
Contributor

@allredj allredj left a comment

Choose a reason for hiding this comment

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

⚠️
This PR failed Diffblue compatibility checks (cbmc commit: 497b740).
Build URL: https://travis-ci.com/diffblue/test-gen/builds/121669324
Status will be re-evaluated on next push.
Common spurious failures include: the cbmc commit has disappeared in the mean time (e.g. in a force-push); the author is not in the list of contributors (e.g. first-time contributors); compatibility was already broken by an earlier merge.

@codecov-io
Copy link

Codecov Report

Merging #4976 into develop will increase coverage by 0.01%.
The diff coverage is 96%.

Impacted file tree graph

@@             Coverage Diff             @@
##           develop    #4976      +/-   ##
===========================================
+ Coverage    69.24%   69.25%   +0.01%     
===========================================
  Files         1309     1309              
  Lines       108453   108497      +44     
===========================================
+ Hits         75096    75138      +42     
- Misses       33357    33359       +2
Impacted Files Coverage Δ
...rc/goto-instrument/goto_instrument_parse_options.h 100% <ø> (ø) ⬆️
src/goto-instrument/code_contracts.cpp 97.85% <100%> (+0.76%) ⬆️
.../goto-instrument/goto_instrument_parse_options.cpp 56.42% <71.42%> (+0.16%) ⬆️

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 cf7b9bf...497b740. Read the comment docs.

@angelhof
Copy link
Contributor Author

angelhof commented Aug 2, 2019

@chrisr-diffblue @martin-cs could you review this PR?

@kroening
Copy link
Member

kroening commented Aug 5, 2019

Seems sensible overall -- perhaps call it --check-postconditions?

@@ -1643,6 +1659,7 @@ void goto_instrument_parse_optionst::help()
" --nondet-static-exclude e same as nondet-static except for the variable e\n" //NOLINT(*)
" (use multiple times if required)\n"
" --check-invariant function instruments invariant checking function\n"
" --assert-ensures function checks that function satisfies its contract ensures after each call\n" // NOLINT(*)
" --remove-pointers converts pointer arithmetic to base+offset expressions\n" // NOLINT(*)
Copy link
Member

Choose a reason for hiding this comment

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

What does "after each call" mean?
So this is really checking the post-conditions in particular calling contexts?

Copy link
Contributor Author

@angelhof angelhof Aug 6, 2019

Choose a reason for hiding this comment

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

@kroening

Seems sensible overall -- perhaps call it --check-postconditions?

I agree that this is a much better name.

What does "after each call" mean?
So this is really checking the post-conditions in particular calling contexts?

It asserts the postcondition after all calls to the function. So it checks the postcondition if the function is reachable, and is called with its name (i.e. it wouldn't work for function pointers). If this is a problem, I could re-implement this as a check before each return point in the function body. I didn't do that initially for simplicity. What do you think?

Copy link
Contributor

Choose a reason for hiding this comment

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

Or maybe --check-ensures (because they are written in the source code as __CPROVER_ensures) or --check-contracts ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

What about --check-contract-postconditions or --check-contract-ensures?

Copy link
Contributor

Choose a reason for hiding this comment

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

My personal opinion is that I like these --check-blah options to mirror whatever is in the source code - so I'd lean towards --check-ensures or --check-contract-ensures - others may have different opinions :-)

@danielsn
Copy link
Contributor

danielsn commented Aug 7, 2019

Would it also make sense to add an check-all-ensures flag to allow this to be turned on for all functions by default? I wouldn't suggest holding this PR up for that, but could be a good second PR.

Copy link
Contributor

@allredj allredj left a comment

Choose a reason for hiding this comment

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

⚠️
This PR failed Diffblue compatibility checks (cbmc commit: 167e36d).
Build URL: https://travis-ci.com/diffblue/test-gen/builds/122308767
Status will be re-evaluated on next push.
Common spurious failures include: the cbmc commit has disappeared in the mean time (e.g. in a force-push); the author is not in the list of contributors (e.g. first-time contributors); compatibility was already broken by an earlier merge.

--assert-ensures min
^EXIT=10$
^SIGNAL=0$
^VERIFICATION FAILED$
Copy link
Contributor

Choose a reason for hiding this comment

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

Could fail for other reasons -- suggest checking the particular failure line you're expecting, and/or comparing against a companion test that doesn't use --assert-ensures and for which verification succeeds

@@ -0,0 +1,7 @@
CORE
main.c
--assert-ensures min
Copy link
Contributor

Choose a reason for hiding this comment

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

Same goes for this test

goto_functionst &_goto_functions):
ns(_symbol_table),
goto_functionst &_goto_functions,
std::list<std::string> _functions_to_assert_ensures)
Copy link
Contributor

Choose a reason for hiding this comment

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

Take by const ref, or std::move into the local variable

assert_ensures_after_calls(goto_functionst::goto_functiont &goto_function);

void
assert_ensures(goto_programt &goto_program, goto_programt::targett target);
Copy link
Contributor

Choose a reason for hiding this comment

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

_after_call for consistency with function above?


// is there a contract?
if(ensures.is_nil())
return goto_assertion;
Copy link
Contributor

Choose a reason for hiding this comment

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

This intends return None, but is vulnerable to accidentally returning a partial program if someone moves some prep work to the top of the function, e.g. allocating a temporary, or labelled SKIP or whatever. Suggest instead this function should return optionalt<goto_programt> so you can use return {} when you intend to report failure / no-action.

// replace formal parameters by arguments, replace return
replace_symbolt replace;

// TODO: return value could be nil
Copy link
Contributor

Choose a reason for hiding this comment

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

? As it just not set at all? I don't think that's valid

code_function_callt::argumentst::const_iterator a_it =
call.arguments().begin();
for(code_typet::parameterst::const_iterator p_it = type.parameters().begin();
p_it != type.parameters().end() && a_it != call.arguments().end();
Copy link
Contributor

Choose a reason for hiding this comment

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

arguments ending before parameters would be a static error AFAIK -- should probably just INVARIANT that you have at least enough arguments (and decide what you want to do for varargs?)

if(mode_assert_ensures)
{
Forall_goto_functions(it, goto_functions)
assert_ensures_after_calls(it->second);
Copy link
Contributor

Choose a reason for hiding this comment

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

Does this share any code with code_contracts? If so perhaps it should be a different pass rather than two modes of the same one

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.

9 participants