-
Notifications
You must be signed in to change notification settings - Fork 273
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
Feature/internal invariant #911
Conversation
src/util/invariant.cpp
Outdated
|
||
\*******************************************************************/ | ||
|
||
static bool output_demangled_name (std::ostream &out, const char * const stack_entry) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lint errors fixed.
src/util/invariant.cpp
Outdated
{ | ||
if (condition) | ||
return; | ||
else |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no else
after return
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK. Fixed.
src/util/invariant.h
Outdated
// Not recommended but provided for backwards compatability | ||
#include <cassert> | ||
|
||
#define INVARIANT(CONDITION, REASON) assert((CONDITION)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could use (CONDITION) && (REASON)
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point.
src/util/invariant.h
Outdated
// 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"); |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Documentation value?
src/util/irep.cpp
Outdated
@@ -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, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
break after (
(several occurrences)
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Split out.
src/util/invariant.cpp
Outdated
|
||
// Backtraces compiler and C library specific | ||
#include <assert.h> | ||
#ifdef __GLIBC__ |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops; fixed.
src/util/invariant.h
Outdated
const bool condition, const char * const reason); | ||
|
||
#define INVARIANT(CONDITION, REASON) \ | ||
check_invariant(__FILE__, __func__, __LINE__, (CONDITION), (REASON)) |
There was a problem hiding this comment.
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 msvc:
https://ci.appveyor.com/project/diffblue/cbmc/build/1.0.1155#L90
There was a problem hiding this comment.
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.
85d2a39
to
5c0a0a0
Compare
The one linter failure is unavoidable; it is a regression test for the linter to find asserts. |
fcea86b
to
3296931
Compare
AppVeyor failure is a bit harder and help from someone who knows windows / has access to a windows machine would be appreciated. |
3296931
to
4676af7
Compare
There was a problem hiding this 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.
src/util/invariant.cpp
Outdated
|
||
#ifdef CPROVER_INVARIANT_PRINT_STACK_TRACE | ||
#include <iostream> | ||
#include <stdlib.h> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use cstdlib
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
src/util/invariant.cpp
Outdated
#include <string> | ||
#include <sstream> | ||
|
||
|
There was a problem hiding this comment.
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
src/util/invariant.cpp
Outdated
#endif | ||
|
||
// Backtraces compiler and C library specific | ||
#include <assert.h> |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added.
src/util/invariant.cpp
Outdated
|
||
\*******************************************************************/ | ||
|
||
static bool output_demangled_name(std::ostream &out, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Newline after (
There was a problem hiding this comment.
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.
src/util/invariant.cpp
Outdated
static bool output_demangled_name(std::ostream &out, | ||
const char * const stack_entry) | ||
{ | ||
bool return_value = false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No whitespace around =
There was a problem hiding this comment.
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.
src/util/invariant.cpp
Outdated
|
||
std::string working(stack_entry); | ||
|
||
size_t start = working.rfind('('); // Path may contain '(' ! |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
src/util/invariant.cpp
Outdated
size_t start = working.rfind('('); // Path may contain '(' ! | ||
size_t end = working.find('+', start); | ||
|
||
if(start != std::string::npos && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No whitespace around !=
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed all.
src/util/invariant.cpp
Outdated
end != std::string::npos && | ||
start + 1 <= end - 1) | ||
{ | ||
size_t length = end - (start + 1); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No whitespace around -, +
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
src/util/invariant.cpp
Outdated
std::string mangled(working.substr(start + 1, length)); | ||
|
||
int demangle_success = 1; | ||
char *demangled = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
std::unique_ptr maybe?
There was a problem hiding this comment.
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.
@martin-cs, you can put |
@peterschrammel Wouldn't that defy the purpose of the regression test? |
My main concern is this:
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 One response to this might be "instead of |
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. |
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 |
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. |
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. |
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.
Please never do this; it is an incredibly bad idea. If we discover
something we thought was an invariant but fails under normal operation
then something is *SERIOUSLY* wrong. This is a huge red flag and
attempting to 'just hack around it' is the *worst* thing we can do and
that is *exactly* why we need a way of building the software that
doesn't throw and exception -- so I can still debug the system remotely,
via e-mail, even when people have done this.
Allow me to clarify some of the terminology:
Invariant
---------
An expression that is true at the given location, regardless of input.
These must hold for *all* inputs. In some deep sense they are a logical
consequence of the structure (and design) of the program. Their failure
is not a "forseeable event"; it's the opposite, it is the programmer
saying "I believe this to be impossible". Although these are
(theoretically) logically redundant, they are of immense value to formal
verification tools ( hence CPROVER_INVARIANT_CPROVER_ASSERT ) but more
importantly they are of immense value to programmers as they formally
document "why is it OK to assume that" / the intent of the design.
So why bother checking them if they are always true? Well, we don't
have to ( CPROVER_INVARIANT_DO_NOT_CHECK ) and as the invariants become
more complex ( "this goto-program is correctly structured"
#751 ), we made want to ignore
them for specific use-cases where we are already checking for known
reference results ( https://sv-comp.sosy-lab.org/ ). However checking
them is useful because it can catch programmer misunderstandings of the
design of the system. This is why they default to being checked. The
point is that there is no one right thing to do once you're reached a
state in which, by definition, the program is wrong. At the moment
invariants are checked / their failure is handled by a mix of different
methods, assert, throw, abort, etc. We should be consistent on marking
them as what they are and separate the /annotation/ from
the /mechanism/.
Assertions
----------
Assertions are one /mechanism/ for handling invariants. They have the
problem that when they fail they are not very informative (this is why I
started working on this) and they aren't particularly useful as
documentation because although they give the condition they don't say
why. Although they aren't great for backwards compatability we should
still offer them ( CPROVER_INVARIANT_ASSERT ).
Exceptions
----------
Exceptions are a /mechanism/ for handling a range of different errors.
This may include handling the failure of invariants. They are a good
mechanism as they allow flexible error handling (this is why they are
the default) but they are not the *only* method for handling errors, nor
are they the most desirable in all circumstances; doubly for handling
invariant failures.
From a support perspective, exceptions can be a nightmare. What I
ideally want is that the program informs me as soon as one of the design
invariants of the system has been broken, ideally statically but
dynamically would do as well. Unfortunately we don't have the tech to
do that (yet). So I'll settle for the next best thing, notification as
soon as the program notices, i.e. as soon as an invariant fails. This
is what CPROVER_INVARIANT_PRINT_STACK_TRACE does, and will give me a
core dump to boot, so I can remotely debug problems on sites I don't
have any interactive access to / interactive access only via e-mail and
someone else.
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. Debugging via e-mail is
hard enough, chasing throws and catches is likely largely impossible.
Thus there needs to be a way of "stop on the first error" that *cannot*
be disabled regardless of what other well-meaning programmers do. Just
saying "we'll tell people not to catch exceptions of type ..." will not
work; we are, by definition, dealing with cases when there are mistakes
during development.
Conclusion
----------
If you believe the design (and correct) implementation of the system
ensures that something holds ---> INVARIANT.
If it is a forseeable error given the correct implementation of the
system ---> throw an exception.
1. I am not saying "don't use exceptions" I am saying "do not *only* use
exceptions to handle failed invariants".
2. This patch is intended to be the start of a long clear-up which
removes asserts and some of the exceptions that are being used to handle
failed invariants ("throw 0" will be my first target). This is part of
a move to not only have documentation on various key data-structure but
to automatically check it.
3. You do not have to use invariants, you can always throw an exception
if it makes you happy. You will make my job harder but if that's the
price for having the tools we need ... so be it.
|
e0ca64c
to
143ab80
Compare
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.
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. |
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? |
On Fri, 2017-05-12 at 06:18 -0700, Reuben Thomas wrote:
> 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'.
There is but in this case I want a hard stop.
This could be achieved equally well using an exception that logs, on construction, the current backtrace into a logfile or to stderr.
This is what messaget is for and why it is suggested that more
comprehensive information goes there.
Good debuggers allow you to set exception breakpoints, which is another option for immediate notification.
In many of the cases where this will be active it is not possible to run
a debugger, good or otherwise.
> 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.
We aren't writing a general purpose library and there are many things in
CPROVER that are just plain wrong. We don't have code that attempts to
recover from invalid / corrupted data structure and there are *many*
higher priority things to write. Yeah, in theory, maybe, one day, we
might want this but let's solve that problem when we come to it and we
know what the actual requirements are. We will have the invariants
clearly marked and separated from the 'normal' exceptions so we can just
drop in the support we need.
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.
No amount of logging will allow me to swap the invariants out for
CPROVER_asserts or ignore them entirely. The specific problem I
referred to was around the normalisation of error and people making
mistakes about exception handling; logging does not fix those either.
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.
Would it help if I changed the (informal) contract to "if this fails
then it is undefined behaviour"?
The only way to combat this is to build and test all the options on CI.
Sure; let's do it.
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.
As I said before, there isn't one right thing to do. It depends on the
real world context of how the system is being deployed. The policy is
pretty clear : write to messaget, then INVARIANT if it is an invariant,
throw an exception if it is a feasible error.
|
5eb96c2
to
265e143
Compare
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? |
There was a problem hiding this 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\] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
depreciated -> deprecated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed; good catch.
scripts/cpplint.py
Outdated
'assert is depreciated, use UNREACHABLE instead') | ||
else: | ||
error(filename, linenum, 'build/depreciated', 4, | ||
'assert is depreciated, use INVARIANT, PRECONDITION, CHECK_RETURN, etc. instead') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
depreciated -> deprecated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed; good catch.
src/util/invariant.cpp
Outdated
#ifdef CPROVER_INVARIANT_PRINT_STACK_TRACE | ||
abort(); | ||
#else | ||
throw std::logic_error(out.str()); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah; probably makes sense.
src/util/invariant.cpp
Outdated
std::size_t i; | ||
|
||
entries=backtrace(stack, sizeof(stack) / sizeof(void *)); | ||
description=backtrace_symbols(stack, entries); |
There was a problem hiding this comment.
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); }
};
src/util/invariant.cpp
Outdated
void * stack[50]; | ||
std::size_t entries; | ||
char **description; | ||
std::size_t i; |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
void *stack[50]={};
?
src/util/invariant.h
Outdated
|
||
void check_invariant(const char * const file, | ||
const char * const function, const int line, | ||
const bool condition, const char * const reason); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
src/util/invariant.cpp
Outdated
std::string mangled(working.substr(start+1, length)); | ||
|
||
int demangle_success=1; | ||
char *demangled= |
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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
.
265e143
to
ab9dd02
Compare
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:
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? |
|
On Thu, 2017-05-18 at 05:02 -0700, Reuben Thomas wrote:
`unique_ptr` is designed to automatically free resources when they go out of scope. I don't really understand what the problem is here.
I just don't want to make the code any more complicated than it needs to
be; especially not to deal with cases that really don't matter given the
context of the application.
I can see an argument for "what happens if an exception is thrown during
the construction of the exception for the fatal error". I guess that
could happen and maybe we would need a meaningful answer for that but...
There are many more pressing things we could spend our time on.
|
@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 ). |
Would it make sense to have that "version" of the patch in this PR? Also a rebase is due. |
ab9dd02
to
f328e08
Compare
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. |
src/util/invariant.h
Outdated
** CPROVER_INVARIANT_* macros. | ||
*/ | ||
|
||
class invariant_failed : public std::logic_error |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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".
There was a problem hiding this comment.
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.
src/util/invariant.h
Outdated
|
||
class invariant_failed : public std::logic_error | ||
{ | ||
public : |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
src/util/invariant.h
Outdated
class invariant_failed : public std::logic_error | ||
{ | ||
public : | ||
explicit invariant_failed (const std::string& what) : logic_error(what) {} |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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) | ||
|
||
|
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
src/util/invariant.cpp
Outdated
|
||
/*******************************************************************\ | ||
|
||
Function: check_invariant |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
src/util/invariant.cpp
Outdated
end!=std::string::npos && | ||
start+1<=end-1) | ||
{ | ||
std::string::size_type length=end - (start+1); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no spaces around -
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
scripts/cpplint.py
Outdated
line = clean_lines.elided[linenum] | ||
match = Match(r'.*\s+assert\(.*\).*', line) | ||
if match: | ||
if Match(r'.*\s+assert\(0\).*', line): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also assert(false)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
src/util/std_expr.h
Outdated
@@ -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, |
There was a problem hiding this comment.
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)
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
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.
f328e08
to
f16c3d8
Compare
Have addressed all of @peterschrammel 's comments, rebased and repushed. |
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.
f16c3d8
to
cbe7de3
Compare
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".