Skip to content

Commit 61a8c30

Browse files
authored
Merge pull request #2666 from NlightNFotis/invariant_changes
Extra documentation and refactoring of the Invariant definitions
2 parents d9d9e2a + 4782b48 commit 61a8c30

File tree

3 files changed

+71
-56
lines changed

3 files changed

+71
-56
lines changed

regression/invariants/driver.cpp

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -40,16 +40,18 @@ class structured_error_testt: public invariant_failedt
4040
const std::string &function,
4141
int line,
4242
const std::string &backtrace,
43+
const std::string &condition,
4344
int code,
44-
const std::string &_description):
45-
invariant_failedt(
46-
file,
47-
function,
48-
line,
49-
backtrace,
50-
pretty_print(code, _description)),
51-
error_code(code),
52-
description(_description)
45+
const std::string &_description)
46+
: invariant_failedt(
47+
file,
48+
function,
49+
line,
50+
backtrace,
51+
condition,
52+
pretty_print(code, _description)),
53+
error_code(code),
54+
description(_description)
5355
{
5456
}
5557
};

src/util/invariant.cpp

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ Author: Martin Brain, [email protected]
88

99
#include "invariant.h"
1010

11-
#include "util/freer.h"
11+
#include "freer.h"
1212

1313
#include <memory>
1414
#include <string>
@@ -79,8 +79,6 @@ void print_backtrace(
7979
std::ostream &out)
8080
{
8181
#ifdef __GLIBC__
82-
out << "Backtrace\n" << std::flush;
83-
8482
void * stack[50] = {};
8583

8684
std::size_t entries=backtrace(stack, sizeof(stack) / sizeof(void *));
@@ -116,20 +114,14 @@ void report_exception_to_stderr(const invariant_failedt &reason)
116114
std::cerr << "--- end invariant violation report ---\n";
117115
}
118116

119-
std::string invariant_failedt::get_invariant_failed_message(
120-
const std::string &file,
121-
const std::string &function,
122-
int line,
123-
const std::string &backtrace,
124-
const std::string &reason)
117+
std::string invariant_failedt::what() const noexcept
125118
{
126119
std::ostringstream out;
127120
out << "Invariant check failed\n"
128-
<< "File " << file
129-
<< " function " << function
130-
<< " line " << line << '\n'
131-
<< "Reason: " << reason
132-
<< "\nBacktrace:\n"
121+
<< "File: " << file << ":" << line << " function: " << function << '\n'
122+
<< "Condition: " << condition << '\n'
123+
<< "Reason: " << reason << '\n'
124+
<< "Backtrace:" << '\n'
133125
<< backtrace << '\n';
134126
return out.str();
135127
}

src/util/invariant.h

Lines changed: 54 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -72,41 +72,32 @@ Author: Martin Brain, [email protected]
7272
/// family of macros, allowing constructs like
7373
/// `INVARIANT(x==y, my_invariantt, (T1)actual1, (T2)actual2, ...)`
7474
///
75-
class invariant_failedt: public std::logic_error
75+
class invariant_failedt
7676
{
7777
private:
78-
std::string get_invariant_failed_message(
79-
const std::string &file,
80-
const std::string &function,
81-
int line,
82-
const std::string &backtrace,
83-
const std::string &reason);
84-
85-
public:
8678
const std::string file;
8779
const std::string function;
8880
const int line;
8981
const std::string backtrace;
82+
const std::string condition;
9083
const std::string reason;
9184

85+
public:
86+
std::string what() const noexcept;
87+
9288
invariant_failedt(
9389
const std::string &_file,
9490
const std::string &_function,
9591
int _line,
9692
const std::string &_backtrace,
97-
const std::string &_reason):
98-
logic_error(
99-
get_invariant_failed_message(
100-
_file,
101-
_function,
102-
_line,
103-
_backtrace,
104-
_reason)),
105-
file(_file),
106-
function(_function),
107-
line(_line),
108-
backtrace(_backtrace),
109-
reason(_reason)
93+
const std::string &_condition,
94+
const std::string &_reason)
95+
: file(_file),
96+
function(_function),
97+
line(_line),
98+
backtrace(_backtrace),
99+
condition(_condition),
100+
reason(_reason)
110101
{
111102
}
112103
};
@@ -149,9 +140,10 @@ void report_exception_to_stderr(const invariant_failedt &);
149140
/// \param file : C string giving the name of the file.
150141
/// \param function : C string giving the name of the function.
151142
/// \param line : The line number of the invariant
143+
/// \param condition : the condition this invariant is checking.
152144
/// \param params : (variadic) parameters to forward to ET's constructor
153145
/// its backtrace member will be set before it is used.
154-
template<class ET, typename ...Params>
146+
template <class ET, typename... Params>
155147
#ifdef __GNUC__
156148
__attribute__((noreturn))
157149
#endif
@@ -160,10 +152,17 @@ invariant_violated_structured(
160152
const std::string &file,
161153
const std::string &function,
162154
const int line,
155+
const std::string &condition,
163156
Params &&... params)
164157
{
165158
std::string backtrace=get_backtrace();
166-
ET to_throw(file, function, line, backtrace, std::forward<Params>(params)...);
159+
ET to_throw(
160+
file,
161+
function,
162+
line,
163+
backtrace,
164+
condition,
165+
std::forward<Params>(params)...);
167166
// We now have a structured exception ready to use;
168167
// in future this is the place to put a 'throw'.
169168
report_exception_to_stderr(to_throw);
@@ -177,20 +176,20 @@ invariant_violated_structured(
177176
/// \param function : C string giving the name of the function.
178177
/// \param line : The line number of the invariant
179178
/// \param reason : brief description of the invariant violation.
179+
/// \param condition : the condition this invariant is checking.
180180
#ifdef __GNUC__
181181
__attribute__((noreturn))
182182
#endif
183-
inline void invariant_violated_string(
183+
inline void
184+
invariant_violated_string(
184185
const std::string &file,
185186
const std::string &function,
186187
const int line,
187-
const std::string &reason)
188+
const std::string &reason,
189+
const std::string &condition)
188190
{
189191
invariant_violated_structured<invariant_failedt>(
190-
file,
191-
function,
192-
line,
193-
reason);
192+
file, function, line, condition, reason);
194193
}
195194

196195
// These require a trailing semicolon by the user, such that INVARIANT
@@ -207,15 +206,23 @@ inline void invariant_violated_string(
207206
{ \
208207
if(!(CONDITION)) \
209208
invariant_violated_string( \
210-
__FILE__, __this_function__, __LINE__, (REASON)); /* NOLINT */ \
209+
__FILE__, \
210+
__this_function__, \
211+
__LINE__, \
212+
(REASON), \
213+
#CONDITION); /* NOLINT */ \
211214
} while(false)
212215

213216
#define INVARIANT_STRUCTURED(CONDITION, TYPENAME, ...) \
214217
do /* NOLINT */ \
215218
{ \
216219
if(!(CONDITION)) \
217220
invariant_violated_structured<TYPENAME>( \
218-
__FILE__, __this_function__, __LINE__, __VA_ARGS__); /* NOLINT */ \
221+
__FILE__, \
222+
__this_function__, \
223+
__LINE__, \
224+
#CONDITION, \
225+
__VA_ARGS__); /* NOLINT */ \
219226
} while(false)
220227

221228
#endif // End CPROVER_DO_NOT_CHECK / CPROVER_ASSERT / ... if block
@@ -225,15 +232,25 @@ inline void invariant_violated_string(
225232
// for INVARIANT.
226233

227234
// The condition should only contain (unmodified) arguments to the method.
235+
// Inputs include arguments passed to the function, as well as member
236+
// variables that the function may read.
228237
// "The design of the system means that the arguments to this method
229238
// will always meet this condition".
239+
//
240+
// The PRECONDITION documents / checks assumptions about the inputs
241+
// that is the caller's responsibility to make happen before the call.
230242
#define PRECONDITION(CONDITION) INVARIANT(CONDITION, "Precondition")
231243
#define PRECONDITION_STRUCTURED(CONDITION, TYPENAME, ...) \
232244
INVARIANT_STRUCTURED(CONDITION, TYPENAME, __VA_ARGS__)
233245

234246
// The condition should only contain variables that will be returned /
235247
// output without further modification.
236248
// "The implementation of this method means that the condition will hold".
249+
//
250+
// A POSTCONDITION documents what the function can guarantee about its
251+
// output when it returns, given that it was called with valid inputs.
252+
// Outputs include the return value of the function, as well as member
253+
// variables that the function may write.
237254
#define POSTCONDITION(CONDITION) INVARIANT(CONDITION, "Postcondition")
238255
#define POSTCONDITION_STRUCTURED(CONDITION, TYPENAME, ...) \
239256
INVARIANT_STRUCTURED(CONDITION, TYPENAME, __VA_ARGS__)
@@ -242,6 +259,10 @@ inline void invariant_violated_string(
242259
// changed by a previous method call.
243260
// "The contract of the previous method call means the following
244261
// condition holds".
262+
//
263+
// A difference between CHECK_RETURN and POSTCONDITION is that CHECK_RETURN is
264+
// a statement about what the caller expects from a function, whereas a
265+
// POSTCONDITION is a statement about guarantees made by the function itself.
245266
#define CHECK_RETURN(CONDITION) INVARIANT(CONDITION, "Check return value")
246267
#define CHECK_RETURN_STRUCTURED(CONDITION, TYPENAME, ...) \
247268
INVARIANT_STRUCTURED(CONDITION, TYPENAME, __VA_ARGS__)
@@ -261,7 +282,7 @@ inline void invariant_violated_string(
261282
// Legacy annotations
262283

263284
// The following should not be used in new code and are only intended
264-
// to migrate documentation and "error handling" in older code
285+
// to migrate documentation and "error handling" in older code.
265286
#define TODO INVARIANT(false, "Todo")
266287
#define UNIMPLEMENTED INVARIANT(false, "Unimplemented")
267288
#define UNHANDLED_CASE INVARIANT(false, "Unhandled case")

0 commit comments

Comments
 (0)