Skip to content

Document and refactor goto check #4611

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 13 commits into from
May 19, 2019
Merged

Conversation

xbauch
Copy link
Contributor

@xbauch xbauch commented May 3, 2019

If you go commit-by-commit it's not so horrible. There is not functional change only a bit of rewording.

  • 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.

throw msg;
}
INVARIANT(
!if_expr.cond().is_boolean(),
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Should have been negated: sorry. Will be fixed.

Copy link
Collaborator

@tautschnig tautschnig left a comment

Choose a reason for hiding this comment

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

This does need cleanup as indicated in the comments, but is overall a good refactoring.

@@ -102,6 +102,8 @@ class goto_checkt
/// operands (or their negations) for recursively calls)
void check_rec_logical_op(const exprt &expr, guardt &guard);

void check_rec_if(const exprt &expr, guardt &guard);
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think this should just take an if_exprt instead of doing the check within the method.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

@@ -110,6 +110,8 @@ class goto_checkt
/// the) if-condition for recursively calls)
void check_rec_if(const exprt &expr, guardt &guard);

void check_rec_member(const exprt &expr, guardt &guard);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Should just take a member_exprt

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

guard.add(not_exprt(op));
else
guard.add(op);
guard.add(expr.id() == ID_or ? not_exprt(op) : op);
Copy link
Collaborator

Choose a reason for hiding this comment

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

If you are touching this, I'd suggest to use boolean_negate(op) instead of not_exprt(op)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

return;
}
check_rec_member(expr, guard);
return;
Copy link
Collaborator

Choose a reason for hiding this comment

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

This return doesn't seem correct to me. I think you should remove it, and also remove the final forall_operands from check_rec_member.

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 return is there only to prevent the forall_operands loop in case the check_rec_member was existed via an explicit return. But it seems to be only an optimisation so I did what you asked for.

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 take it back. Avoiding the recursive call on the member expression operands may be just an optimisation but one of our regression tests (Malloc23) requires exactly 4 failing assertions. If we change this section so that the recursion is always called, this test fails.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Which ... might be a bugfix? Does it generate more assertions or is it missing some?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Doesn't seem so:

struct A
{
  int x;
  int y;
};

int main()
{
  struct A *p=malloc(sizeof(int));
  p->x=0; // OK
  p->y=0; // out of bounds
..

Unless we skip the recursive call, we would generate a pointer check for p->ok=0 which, on it's own, would fail.

Copy link
Collaborator

Choose a reason for hiding this comment

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

But still the behaviour before and after isn't the same: previously, we only returned in some cases, now we always return early.

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, but since I included the recursive call on sub-expressions in rec_check_member the only thing we skip by returning early is the remaining case analyses none of which can succeed (since we know that expr.id()==ID_member).

Copy link
Collaborator

Choose a reason for hiding this comment

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

Fair enough, I was just a bit worried that one day someone would add a case. But I suppose testing (or rather: failing tests) should catch that. So it seems fine as is.

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 thought about it a bit and tried having the check_rec_member return a Boolean indicating if the subsequent analysis should be attempted or not: d6f7f05.

/// NaN (for floating point numbers).
/// \param expr: the expression to be checked
/// \param guard: the condition for the check (unmodified here)
void check_rec_div(const exprt &expr, guardt &guard);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Should take a div_exprt

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

guarded_expr = implies_exprt(guard.as_expr(), std::move(simplified_expr));
exprt guarded_expr =
guard.is_true()
? simplified_expr
Copy link
Collaborator

Choose a reason for hiding this comment

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

std::move(simplified_expr)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

enable_assert_to_assume ? goto_programt::make_assumption(
std::move(guarded_expr), source_location)
: goto_programt::make_assertion(
std::move(guarded_expr), source_location));
Copy link
Collaborator

Choose a reason for hiding this comment

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

There is another instance of this pattern (around line 1800) - just search for "ASSUME". Could you please fix this as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added.

it=next;
it = assertions.erase(it);
else
++it;
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

What about using std::remove_if (until we can use erase_if with C++20)?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Unfortunately, std::remove_if requires the iterator to be MoveAssignable. Will have to wait for this one.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Ah, ok, too bad.

Copy link
Member

@peterschrammel peterschrammel left a comment

Choose a reason for hiding this comment

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

lgtm modulo squashing promised

/// \param source_location: the source location of the original expression
/// \param src_expr: the original expression to be included in the comment
/// \param guard: the condition under which the asserted expression should be
/// taken into account
void add_guarded_claim(
Copy link
Member

Choose a reason for hiding this comment

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

While we are here, can we rename claim to property since we have replaced that terminology in other places in the codebase?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

@@ -194,6 +199,10 @@ class goto_checkt
typedef std::set<exprt> assertionst;
assertionst assertions;

/// Remove all assertions containing they symbol in \p lhs as well as all
Copy link
Member

Choose a reason for hiding this comment

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

they -> the

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

@xbauch xbauch force-pushed the refactor/goto-check branch from d6f7f05 to aa16fd6 Compare May 19, 2019 07:18
petr-bauch added 12 commits May 19, 2019 09:17
from check_rec into stand-alone function. Getting rid of the flag.
Using the checks inside to_expr methods.
into a stand-alone function.
Using invariants instead of exceptions.
Using invariants, to-conversion and a bit of clang-formatting.
Only clang-format, inline expr-construction, and for-each loop.
Into check_rec_div and check_rec_arithmetic_op.
Using the initializer-list constructor for sets and and standard idiom for
erase_if.
instead of instructiont constructor.
to negate boolean expression and to get address_of expression.
to collect_allocations, check_rec, and check.
for the bits we touched in this PR.
@xbauch xbauch force-pushed the refactor/goto-check branch from d62fe0a to b5060ee Compare May 19, 2019 08:17
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: d62fe0a).
Build URL: https://travis-ci.com/diffblue/test-gen/builds/112345526
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.

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.

✔️
Passed Diffblue compatibility checks (cbmc commit: b5060ee).
Build URL: https://travis-ci.com/diffblue/test-gen/builds/112346598

@xbauch xbauch merged commit 230e525 into diffblue:develop May 19, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants