Skip to content

Feature/internal invariant #911

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 8 commits into from
Jun 13, 2017

Conversation

martin-cs
Copy link
Collaborator

A proposal to resolve issue #182 . Assertions (and things like throw 0) should be replaced with INVARIANT, PRECONDITION, POSTCONDITION, DATA_INVARIANT, CHECK_RETURN and UNREACHABLE. These macros can be configured to throw a std::exception with a backtrace (the default), assert, __CPROVER_assert (so we can examine our own source code), be ignored (for performance or checking they are side-effect free) or print out the backtrace and then terminate.

To get backtraces you need to build against glibc. To get function names in the backtrace you must have built with LINKFLAGS="-rdynamic".

@martin-cs martin-cs mentioned this pull request May 11, 2017

\*******************************************************************/

static bool output_demangled_name (std::ostream &out, const char * const stack_entry)
Copy link
Member

Choose a reason for hiding this comment

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

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Lint errors fixed.

{
if (condition)
return;
else
Copy link
Member

Choose a reason for hiding this comment

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

no else after return

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

OK. Fixed.

// Not recommended but provided for backwards compatability
#include <cassert>

#define INVARIANT(CONDITION, REASON) assert((CONDITION))
Copy link
Member

Choose a reason for hiding this comment

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

could use (CONDITION) && (REASON)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes; good idea. Fixed.

// This is *not* recommended as it can result in unpredictable behaviour
// including silently reporting incorrect results.
// This is also useful for checking side-effect freedom.
#define INVARIANT(CONDITION, REASON) do {} while(0)
Copy link
Member

Choose a reason for hiding this comment

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

ok, ; would do as well

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I put do/while to swallow the semi-colon at the end.

Copy link
Collaborator

Choose a reason for hiding this comment

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

To add to this: it's important to ensure that no expansion of those macros can yield ;; as that may trigger compiler warnings.

Copy link
Member

Choose a reason for hiding this comment

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

Good point.

// to migrate documentation and "error handling" in older code
#define TODO INVARIANT(0, "Todo");
#define UNIMPLEMENTED INVARIANT(0, "Unimplemented");
#define UNHANDLED_CASE INVARIANT(0, "Unhandled case");
Copy link
Member

Choose a reason for hiding this comment

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

What's the difference between these?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Documentation value?

@@ -203,7 +204,8 @@ void irept::nonrecursive_destructor(dt *old_data)
if(d==&empty_d)
continue;

assert(d->ref_count!=0);
INVARIANT(d->ref_count!=0,
Copy link
Member

Choose a reason for hiding this comment

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

break after ( (several occurrences)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Unnecessary line break removed. Is that what you meant?

@@ -42,7 +41,7 @@ Author: Daniel Kroening, [email protected]
#define UNEXPECTEDCASE(S) throw "Unexpected case: " S

// General todos
#define TODO(S) throw "TODO: " S
#define SMT2_TODO(S) throw "TODO: " S
Copy link
Member

Choose a reason for hiding this comment

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

Why is this not a TODO or UNIMPLEMENTED?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I wanted this commit to be as minimal as possible and to be clearly not changing functionality (the whole set should be very low risk / minimal impact). To replace with the new TODO I'd have to move the more complex error messages to use error(), which would mean a bigger change.

Copy link
Member

Choose a reason for hiding this comment

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

Ok, this can be done separately.

@@ -1914,6 +2001,34 @@ class bitnot_exprt:public unary_exprt
}
};

/*! \brief Cast a generic exprt to a \ref bitnot_exprt
Copy link
Member

Choose a reason for hiding this comment

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

These two functions seem to be new. Maybe put into a separate commit.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Split out.


// Backtraces compiler and C library specific
#include <assert.h>
#ifdef __GLIBC__
Copy link
Member

Choose a reason for hiding this comment

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

This doesn't work for mac and alpine:
https://travis-ci.org/diffblue/cbmc/jobs/231294968#L140

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Oops; fixed.

const bool condition, const char * const reason);

#define INVARIANT(CONDITION, REASON) \
check_invariant(__FILE__, __func__, __LINE__, (CONDITION), (REASON))
Copy link
Member

Choose a reason for hiding this comment

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

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It's in C99! But, ofcourse, Microsoft can't ship a standard compliant C compiler...
Fixed.

@martin-cs martin-cs force-pushed the feature/internal-invariant branch 2 times, most recently from 85d2a39 to 5c0a0a0 Compare May 11, 2017 23:35
@martin-cs
Copy link
Collaborator Author

martin-cs commented May 11, 2017

The one linter failure is unavoidable; it is a regression test for the linter to find asserts.

@martin-cs martin-cs force-pushed the feature/internal-invariant branch 2 times, most recently from fcea86b to 3296931 Compare May 12, 2017 00:47
@martin-cs
Copy link
Collaborator Author

AppVeyor failure is a bit harder and help from someone who knows windows / has access to a windows machine would be appreciated.

@martin-cs martin-cs force-pushed the feature/internal-invariant branch from 3296931 to 4676af7 Compare May 12, 2017 01:18
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.

Very nice work! My comments are all about whitespace... I'd suggest that @reuk takes a look at this.


#ifdef CPROVER_INVARIANT_PRINT_STACK_TRACE
#include <iostream>
#include <stdlib.h>
Copy link
Collaborator

Choose a reason for hiding this comment

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

Use cstdlib

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed.

#include <string>
#include <sstream>


Copy link
Collaborator

Choose a reason for hiding this comment

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

A lot of blank lines here, I'd opt for single blank lines

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed.

#endif

// Backtraces compiler and C library specific
#include <assert.h>
Copy link
Collaborator

Choose a reason for hiding this comment

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

Should be cassert, I'd say

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

That won't do what I'm after. I'm including a C header so that I get GLIBC defined if that C library is GLibc. Thus I want something that is definitely and always a C header, regardless of how the stdc++ library is implemented.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Ah! Would you mind adding this as a comment to the code so that no one ever changes this in a naive cleanup attempt? Thanks!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Added.


\*******************************************************************/

static bool output_demangled_name(std::ostream &out,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Newline after (

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Ah; that's what Peter wanted. Sorry, not checked by lint.

static bool output_demangled_name(std::ostream &out,
const char * const stack_entry)
{
bool return_value = false;
Copy link
Collaborator

Choose a reason for hiding this comment

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

No whitespace around =

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed. Again, I thought lint was catching these so when it wasn't caught I figured we weren't doing it.


std::string working(stack_entry);

size_t start = working.rfind('('); // Path may contain '(' !
Copy link
Collaborator

Choose a reason for hiding this comment

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

We seem to be widely using std::size_t, even though in this case I'd even lean towards std::string::size_type

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed.

size_t start = working.rfind('('); // Path may contain '(' !
size_t end = working.find('+', start);

if(start != std::string::npos &&
Copy link
Collaborator

Choose a reason for hiding this comment

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

No whitespace around !=

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed all.

end != std::string::npos &&
start + 1 <= end - 1)
{
size_t length = end - (start + 1);
Copy link
Collaborator

Choose a reason for hiding this comment

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

No whitespace around -, +

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed.

std::string mangled(working.substr(start + 1, length));

int demangle_success = 1;
char *demangled =
Copy link
Collaborator

Choose a reason for hiding this comment

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

std::unique_ptr maybe?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I'd rather keep these as free as the documentation says that they are explicitly malloc'd.

@peterschrammel
Copy link
Member

@martin-cs, you can put // NOLINT(build/depreciated) to silence the linter for this specific case.

@tautschnig
Copy link
Collaborator

@peterschrammel Wouldn't that defy the purpose of the regression test?

@reuk
Copy link
Contributor

reuk commented May 12, 2017

My main concern is this:

These macros can be configured to throw a std::exception with a backtrace (the default), assert, __CPROVER_assert (so we can examine our own source code), be ignored (for performance or checking they are side-effect free) or print out the backtrace and then terminate.

The exceptions that may be thrown from a function are an important part of its interface. Lets say that, during normal development, we build with invariants set to throw exceptions. Somewhere in the code, we discover that something that was designed to be an invariant actually may trigger during normal operation, so we use a try/catch to retry this method with different arguments if it fails. Now, if we swap exceptions for asserts, we get a hard crash. Worse, if we remove them, we open the program up to memory corruption errors, because the program attempts to continue. Being able to swap out error reporting mechanisms means that the program might exhibit markedly different behaviour depending on the compile flags, which in turn means that we would need (at minimum) every error reporting mechanism to be enabled and tested during CI.

One response to this might be "instead of try/catch on an exception triggered by an assertion, the invariant should be replaced by a true (non-invariant) exception", but part of the reason this is an issue in the first place is because we're hitting assertions in production. We haven't been able to guarantee that the things we think are invariants really are. If we just used banned assert and used exceptions by default, we wouldn't have to worry about putting side-effects in assert statements, we'd always have the option to recover from errors (the caller should be allowed to decide whether or not an error from higher up the stack is really fatal or not), and we'd only have a single build configuration to test.

@tautschnig
Copy link
Collaborator

The exceptions that may be thrown from a function are an important part of its interface. Lets say that, during normal development, we build with invariants set to throw exceptions. Somewhere in the code, we discover that something that was designed to be an invariant actually may trigger during normal operation, so we use a try/catch to retry this method with different arguments if it fails.

That's abuse of exceptions, isn't it? If I read this correctly, this would suggest that exceptions are considered part of normal behaviour. But I may be misinterpreting what you've said.

@reuk
Copy link
Contributor

reuk commented May 12, 2017

Exceptions aren't normal behaviour, but they are foreseeable program states. In the situation described in this comment, we might want to try loading other classes, even if one class is ill-formed - we might be able to use the default stubbing behaviour for classes which couldn't be loaded (for example). Using try/catch to handle errors and continue with execution isn't an abuse, but rather the motivating case for exceptions.

@tautschnig
Copy link
Collaborator

Agreed, but then we should be throwing very specific types/classes of exceptions in all forseen cases. We can then catch those, and only those.

@reuk
Copy link
Contributor

reuk commented May 12, 2017

Yes, that's my proposal. But I'm saying that currently, everywhere we're using assertions, we could make the decision to replace these with exceptions, as this gives us a chance to recover from logic errors. Inner functions shouldn't have the ability to halt the program with no warnings, because there's always a chance that the caller may have forseen the failure case and have a contingency plan.

@martin-cs
Copy link
Collaborator Author

martin-cs commented May 12, 2017 via email

@martin-cs martin-cs force-pushed the feature/internal-invariant branch 2 times, most recently from e0ca64c to 143ab80 Compare May 12, 2017 12:44
@reuk
Copy link
Contributor

reuk commented May 12, 2017

So I'll settle for the next best thing, notification as soon as the program notices, i.e. as soon as an invariant fails.

Sure, but there's a difference between 'notification' and 'terminate the program'. This could be achieved equally well using an exception that logs, on construction, the current backtrace into a logfile or to stderr. Good debuggers allow you to set exception breakpoints, which is another option for immediate notification.

If you only handle invariant failure by an exception then there is a risk that someone will catch it, think they know better, do something else and so on... leaving me staring at an incorrect answer with no real
idea where, when or why the program is wrong.

Maybe the caller really does know better. As the writer of a library function, you have no way of knowing all the contexts in which your function will be called. As I see it, all the arguments for assertions could equally well be addressed with better logging, as the main problem seems to be a lack of information when invariants are broken.

Going back to my original point, allowing the error notification mechanism to be changed at compile time may cause breakage if callers come to depend on the behaviour of a particular mechanism. The only way to combat this is to build and test all the options on CI. Therefore, I believe that this should not be a point of customisation. Instead, we should decide and document how each class of error should be reported, in order to minimise the number of build configurations.

@tautschnig
Copy link
Collaborator

Going back to my original point, allowing the error notification mechanism to be changed at compile time may cause breakage if callers come to depend on the behaviour of a particular mechanism. The only way to combat this is to build and test all the options on CI. Therefore, I believe that this should not be a point of customisation. Instead, we should decide and document how each class of error should be reported, in order to minimise the number of build configurations.

I think I no longer know what the problem is here. Let's just build all the config options in CI and get over it?

@martin-cs
Copy link
Collaborator Author

martin-cs commented May 12, 2017 via email

@martin-cs martin-cs force-pushed the feature/internal-invariant branch 3 times, most recently from 5eb96c2 to 265e143 Compare May 17, 2017 22:26
@martin-cs
Copy link
Collaborator Author

This is now as fixed as it can be. There is one linter failure, from a regression test for the linter to check for exactly that kind of failure.

@kroening : is this what you wanted when you told me an assertion based solution?
@peterschrammel @tautschnig : any more review comments?

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.

From my point of view this looks good, even though I think 14118e5 would better be placed in a separate (trivial) PR. But that's just me doing my usual nit picking.

CORE
main.cpp

^main\.cpp:8: assert is depreciated, use INVARIANT instead \[build/depreciated\] \[4\]
Copy link
Contributor

Choose a reason for hiding this comment

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

depreciated -> deprecated

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed; good catch.

'assert is depreciated, use UNREACHABLE instead')
else:
error(filename, linenum, 'build/depreciated', 4,
'assert is depreciated, use INVARIANT, PRECONDITION, CHECK_RETURN, etc. instead')
Copy link
Contributor

Choose a reason for hiding this comment

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

depreciated -> deprecated

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed; good catch.

#ifdef CPROVER_INVARIANT_PRINT_STACK_TRACE
abort();
#else
throw std::logic_error(out.str());
Copy link
Contributor

Choose a reason for hiding this comment

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

Throw something more descriptive than logic_error, probably a custom exception derived from logic_error specifically for logic errors in CBMC code.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yeah; probably makes sense.

std::size_t i;

entries=backtrace(stack, sizeof(stack) / sizeof(void *));
description=backtrace_symbols(stack, entries);
Copy link
Contributor

@reuk reuk May 18, 2017

Choose a reason for hiding this comment

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

Use unique_ptr with a custom deleter. This deleter is a general-purpose component that should probably live in util:

struct freert final {
  template <typename T> void operator()(T t) const { free(t); }
};

void * stack[50];
std::size_t entries;
char **description;
std::size_t i;
Copy link
Contributor

@reuk reuk May 18, 2017

Choose a reason for hiding this comment

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

All of these should be initialised, and moved as close to where they are used as possible.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Refactored all but stack so that the are declared and initialised together. stack is initialised on the line below it's declaration.

Copy link
Contributor

Choose a reason for hiding this comment

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

void *stack[50]={};?


void check_invariant(const char * const file,
const char * const function, const int line,
const bool condition, const char * const reason);
Copy link
Contributor

Choose a reason for hiding this comment

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

Marking passed-by-value arguments const in the interface has no effect.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Having them const stops me accidentally changing them in the implementation.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, but it's unnecessary noise on the interface. This post explains it pretty well.

It's valid to have void check_invariant(bool); in your interface and void check_invariant(const bool) {} in your implementation. The declaration will still correspond to the expected definition.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I think it's worth keeping the signatures the same in declaration and implementation. If the cost of getting people to add consts is that we get too many -- so be it. I really doubt the consts are used minimally in the rest of the code base.

Plus; this is not a developer facing function. If there was a way of hiding the declaration entirely; then I'd do it.

std::string mangled(working.substr(start+1, length));

int demangle_success=1;
char *demangled=
Copy link
Contributor

Choose a reason for hiding this comment

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

Use unique_ptr with custom freer.


if(demangle_success==0)
{
out << working.substr(0, start+1)
Copy link
Contributor

Choose a reason for hiding this comment

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

If the ostream has exceptions enabled, << may throw an exception, in which case we would leak demangled.

@martin-cs martin-cs force-pushed the feature/internal-invariant branch from 265e143 to ab9dd02 Compare May 18, 2017 10:48
@martin-cs
Copy link
Collaborator Author

The memory stuff... I think I'd rather keep the code obvious and straight forward. As I see it, the only possibility of a leak would requires the following sequence:

  1. an invariant fails.
  2. writing to a std::ostringstream throws an exception

That sounds pretty terminally unrecoverable to me and at that point I'm not sure that leaking a small number of bytes is really a problem? I mean, I could try/catch around the writing so that we still get a meaningful error message even if it goes catastrophically wrong. Would that help?

@reuk
Copy link
Contributor

reuk commented May 18, 2017

unique_ptr is designed to automatically free resources when they go out of scope. I don't really understand what the problem is here.

@martin-cs
Copy link
Collaborator Author

martin-cs commented May 18, 2017 via email

@tautschnig tautschnig mentioned this pull request Jun 5, 2017
@martin-cs
Copy link
Collaborator Author

@kroening A version of this patch has been used to locate an issue that was not otherwise possible to diagnose ( https://github.com/diffblue/platform/issues/603 ).

@tautschnig
Copy link
Collaborator

Would it make sense to have that "version" of the patch in this PR? Also a rebase is due.

@martin-cs martin-cs force-pushed the feature/internal-invariant branch from ab9dd02 to f328e08 Compare June 9, 2017 10:47
@martin-cs
Copy link
Collaborator Author

Have rebased.

The version of this patch... I'd rather not because 1. it patches a different version to trunk (older plus some things that are not in master) and 2. it contains a hasty, automatic conversion of most of the asserts and some of the throws to invariants. I don't think automatic conversion is the way to go as part of the point of this exercise is to tidy up what are actually invariants, what are possible error cases that should be thrown, etc.

** CPROVER_INVARIANT_* macros.
*/

class invariant_failed : public std::logic_error
Copy link
Member

Choose a reason for hiding this comment

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

The linter wants you to remove the space before the colon. Same in 57 and 58.

Copy link
Member

Choose a reason for hiding this comment

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

The class name should end in "t".

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed; could have sworn I'd got all of the linting errors.


class invariant_failed : public std::logic_error
{
public :
Copy link
Member

Choose a reason for hiding this comment

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

No space before colon. Do not indent public.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed.

class invariant_failed : public std::logic_error
{
public :
explicit invariant_failed (const std::string& what) : logic_error(what) {}
Copy link
Member

Choose a reason for hiding this comment

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

No space before (. Same in the next line.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed.

// This is *not* recommended as it can result in unpredictable behaviour
// including silently reporting incorrect results.
// This is also useful for checking side-effect freedom.
#define INVARIANT(CONDITION, REASON) do {} while(0)
Copy link
Member

Choose a reason for hiding this comment

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

Good point.

#define INVARIANT(CONDITION, REASON) \
__CPROVER_assert((CONDITION), "Invariant : " REASON)


Copy link
Member

Choose a reason for hiding this comment

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

Reduce the number of blank lines in this file.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done.


/*******************************************************************\

Function: check_invariant
Copy link
Member

Choose a reason for hiding this comment

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

Should be adapted do doxygen now.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done.

end!=std::string::npos &&
start+1<=end-1)
{
std::string::size_type length=end - (start+1);
Copy link
Member

Choose a reason for hiding this comment

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

no spaces around -

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed.

line = clean_lines.elided[linenum]
match = Match(r'.*\s+assert\(.*\).*', line)
if match:
if Match(r'.*\s+assert\(0\).*', line):
Copy link
Member

Choose a reason for hiding this comment

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

also assert(false)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed.

@@ -59,7 +58,9 @@ class transt:public exprt
*/
inline const transt &to_trans_expr(const exprt &expr)
{
assert(expr.id()==ID_trans && expr.operands().size()==3);
PRECONDITION(expr.id()==ID_trans);
DATA_INVARIANT(expr.operands().size()==3,
Copy link
Member

Choose a reason for hiding this comment

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

Put both arguments on a separate line if they don't fit (similar cases below)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed all.

@@ -245,7 +245,7 @@ bool exprt::is_zero() const
{
rationalt rat_value;
if(to_rational(*this, rat_value))
assert(false);
CHECK_RETURN(false);
Copy link
Member

Choose a reason for hiding this comment

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

It would actually be even more convenient if we could write CHECK_RETURN(to_rational(*this, rat_value));. However, we must guarantee that the implementation of CHECK_RETURN always evaluates its argument.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes... this patch set is intended to replace assert but there are a few other invariant handling patterns that could benefit from some kind of support. This is one of them, another is:

if (invariant) {
  debug() << "Actually helpful message with " << variables << " in it "
                << "maybe even " << e.pretty();
  assert(0); 
}

We should do something about these but I don't think we should do so in this patch set. I this will come out of trying to convert things to this way of doing things. We will get a better idea of what patterns there are and how they could be improved.

martin added 2 commits June 13, 2017 10:51
This allows us to have tests that are intended to abort.
Given that tests can (and mostly do) check the exit code this should
not reduce the utility of the test system.
@martin-cs martin-cs force-pushed the feature/internal-invariant branch from f328e08 to f16c3d8 Compare June 13, 2017 10:59
@martin-cs
Copy link
Collaborator Author

Have addressed all of @peterschrammel 's comments, rebased and repushed.

martin added 6 commits June 13, 2017 16:11
Many of these are split into two invariants are there is the specific
precondition (before calling to_div_expr it is your responsibility to
check you have an exprt with id() == ID_div) and a general data structure
invariant (all ID_div exprts should have exactly two operands).

A few invariants are commented out as I believe they hold but they are
not currently enforced.  This commit should not change the functionality
of the code.
These also serve as examples of which annotations to use.
@martin-cs martin-cs force-pushed the feature/internal-invariant branch from f16c3d8 to cbe7de3 Compare June 13, 2017 15:12
@kroening kroening merged commit ae74767 into diffblue:master Jun 13, 2017
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.

5 participants