9
9
#ifndef CPROVER_UTIL_INVARIANT_H
10
10
#define CPROVER_UTIL_INVARIANT_H
11
11
12
+ #include < cstdlib>
12
13
#include < stdexcept>
13
- #include < type_traits>
14
14
#include < string>
15
- #include < cstdlib>
15
+ #include < tuple>
16
+ #include < type_traits>
16
17
17
18
/*
18
19
** Invariants document conditions that the programmer believes to
@@ -83,7 +84,7 @@ class invariant_failedt
83
84
const std::string reason;
84
85
85
86
public:
86
- std::string what () const noexcept ;
87
+ virtual std::string what () const noexcept ;
87
88
88
89
invariant_failedt (
89
90
const std::string &_file,
@@ -102,9 +103,47 @@ class invariant_failedt
102
103
}
103
104
};
104
105
106
+ // / A class that includes diagnostic information related to an invariant
107
+ // / violation.
108
+ class invariant_with_diagnostics_failedt : public invariant_failedt
109
+ {
110
+ private:
111
+ const std::string diagnostics;
112
+
113
+ public:
114
+ virtual std::string what () const noexcept ;
115
+
116
+ invariant_with_diagnostics_failedt (
117
+ const std::string &_file,
118
+ const std::string &_function,
119
+ int _line,
120
+ const std::string &_backtrace,
121
+ const std::string &_condition,
122
+ const std::string &_reason,
123
+ const std::string &_diagnostics)
124
+ : invariant_failedt(
125
+ _file,
126
+ _function,
127
+ _line,
128
+ _backtrace,
129
+ _condition,
130
+ _reason),
131
+ diagnostics (_diagnostics)
132
+ {
133
+ }
134
+ };
135
+
136
+ // The macros MACRO<n> (e.g., INVARIANT2) are for internal use in this file
137
+ // only. The corresponding macros that take a variable number of arguments (see
138
+ // further below) should be used instead, which in turn call those with a fixed
139
+ // number of arguments. For example, if INVARIANT(...) is called with two
140
+ // arguments, it calls INVARIANT2().
141
+
105
142
#if defined(CPROVER_INVARIANT_CPROVER_ASSERT)
106
143
// Used to allow CPROVER to check itself
107
- #define INVARIANT (CONDITION, REASON ) \
144
+ #define INVARIANT2 (CONDITION, REASON ) \
145
+ __CPROVER_assert ((CONDITION), "Invariant : " REASON)
146
+ #define INVARIANT3 (CONDITION, REASON, DIAGNOSTICS ) \
108
147
__CPROVER_assert ((CONDITION), "Invariant : " REASON)
109
148
110
149
#define INVARIANT_STRUCTURED (CONDITION, TYPENAME, ...) \
@@ -115,14 +154,23 @@ class invariant_failedt
115
154
// This is *not* recommended as it can result in unpredictable behaviour
116
155
// including silently reporting incorrect results.
117
156
// This is also useful for checking side-effect freedom.
118
- #define INVARIANT (CONDITION, REASON ) do {} while (false )
157
+ #define INVARIANT2 (CONDITION, REASON ) \
158
+ do \
159
+ { \
160
+ } while (false )
161
+ #define INVARIANT3 (CONDITION, REASON, DIAGNOSTICS ) \
162
+ do \
163
+ { \
164
+ } while (false )
119
165
#define INVARIANT_STRUCTURED (CONDITION, TYPENAME, ...) do {} while (false )
120
166
121
167
#elif defined(CPROVER_INVARIANT_ASSERT)
122
168
// Not recommended but provided for backwards compatability
123
169
#include < cassert>
124
170
// NOLINTNEXTLINE(*)
125
- #define INVARIANT (CONDITION, REASON ) assert((CONDITION) && ((REASON), true ))
171
+ #define INVARIANT2 (CONDITION, REASON ) assert((CONDITION) && ((REASON), true ))
172
+ #define INVARIANT3 (CONDITION, REASON, DIAGNOSTICS ) \
173
+ assert ((CONDITION) && ((REASON), true)) /* NOLINT */
126
174
// NOLINTNEXTLINE(*)
127
175
#define INVARIANT_STRUCTURED (CONDITION, TYPENAME, ...) assert((CONDITION))
128
176
#else
@@ -185,8 +233,8 @@ invariant_violated_string(
185
233
const std::string &file,
186
234
const std::string &function,
187
235
const int line,
188
- const std::string &reason ,
189
- const std::string &condition )
236
+ const std::string &condition ,
237
+ const std::string &reason )
190
238
{
191
239
invariant_violated_structured<invariant_failedt>(
192
240
file, function, line, condition, reason);
@@ -201,16 +249,46 @@ invariant_violated_string(
201
249
#define __this_function__ __func__
202
250
#endif
203
251
204
- #define INVARIANT (CONDITION, REASON ) \
252
+ #define GET_MACRO (X1, X2, X3, X4, X5, X6, MACRO, ...) MACRO
253
+
254
+ #define REDIRECT (MACRO, ...) \
255
+ do \
256
+ { \
257
+ GET_MACRO ( \
258
+ __VA_ARGS__, \
259
+ MACRO##6 , \
260
+ MACRO##5 , \
261
+ MACRO##4 , \
262
+ MACRO##3 , \
263
+ MACRO##2 , \
264
+ MACRO##1 , \
265
+ DUMMY_MACRO_ARG) \
266
+ (__VA_ARGS__); \
267
+ } while (false )
268
+
269
+ #define INVARIANT2 (CONDITION, REASON ) \
205
270
do /* NOLINT */ \
206
271
{ \
207
272
if (!(CONDITION)) \
208
273
invariant_violated_string ( \
209
274
__FILE__, \
210
275
__this_function__, \
211
276
__LINE__, \
277
+ #CONDITION, \
278
+ (REASON)); /* NOLINT */ \
279
+ } while (false )
280
+
281
+ #define INVARIANT3 (CONDITION, REASON, DIAGNOSTICS ) \
282
+ do /* NOLINT */ \
283
+ { \
284
+ if (!(CONDITION)) \
285
+ invariant_violated_structured<invariant_with_diagnostics_failedt>( \
286
+ __FILE__, \
287
+ __this_function__, \
288
+ __LINE__, \
289
+ #CONDITION, \
212
290
(REASON), \
213
- #CONDITION) ; /* NOLINT */ \
291
+ (DIAGNOSTICS)) ; /* NOLINT */ \
214
292
} while (false )
215
293
216
294
#define INVARIANT_STRUCTURED (CONDITION, TYPENAME, ...) \
@@ -227,19 +305,25 @@ invariant_violated_string(
227
305
228
306
#endif // End CPROVER_DO_NOT_CHECK / CPROVER_ASSERT / ... if block
229
307
230
- // Short hand macros. The second variant of each one permits including an
231
- // explanation or structured exception, in which case they are synonyms
232
- // for INVARIANT.
308
+ // Short hand macros. The variants *_STRUCTURED below allow to specify a custom
309
+ // exception, and are equivalent to INVARIANT_STRUCTURED.
310
+
311
+ #define INVARIANT (...) REDIRECT(INVARIANT, __VA_ARGS__)
233
312
234
- // 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.
313
+ // The condition should only contain (unmodified) inputs to the method. Inputs
314
+ // include arguments passed to the function, as well as member variables that
315
+ // the function may read.
237
316
// "The design of the system means that the arguments to this method
238
317
// will always meet this condition".
239
318
//
240
319
// The PRECONDITION documents / checks assumptions about the inputs
241
320
// that is the caller's responsibility to make happen before the call.
242
- #define PRECONDITION (CONDITION ) INVARIANT(CONDITION, " Precondition" )
321
+ #define PRECONDITION1 (CONDITION ) INVARIANT2(CONDITION, " Precondition" )
322
+ #define PRECONDITION2 (CONDITION, DIAGNOSTICS ) \
323
+ INVARIANT3 (CONDITION, " Precondition" , DIAGNOSTICS)
324
+
325
+ #define PRECONDITION (...) REDIRECT(PRECONDITION, __VA_ARGS__)
326
+
243
327
#define PRECONDITION_STRUCTURED (CONDITION, TYPENAME, ...) \
244
328
INVARIANT_STRUCTURED (CONDITION, TYPENAME, __VA_ARGS__)
245
329
@@ -251,7 +335,12 @@ invariant_violated_string(
251
335
// output when it returns, given that it was called with valid inputs.
252
336
// Outputs include the return value of the function, as well as member
253
337
// variables that the function may write.
254
- #define POSTCONDITION (CONDITION ) INVARIANT(CONDITION, " Postcondition" )
338
+ #define POSTCONDITION1 (CONDITION ) INVARIANT2(CONDITION, " Postcondition" )
339
+ #define POSTCONDITION2 (CONDITION, DIAGNOSTICS ) \
340
+ INVARIANT3 (CONDITION, " Postcondition" , DIAGNOSTICS)
341
+
342
+ #define POSTCONDITION (...) REDIRECT(POSTCONDITION, __VA_ARGS__)
343
+
255
344
#define POSTCONDITION_STRUCTURED (CONDITION, TYPENAME, ...) \
256
345
INVARIANT_STRUCTURED (CONDITION, TYPENAME, __VA_ARGS__)
257
346
@@ -263,28 +352,38 @@ invariant_violated_string(
263
352
// A difference between CHECK_RETURN and POSTCONDITION is that CHECK_RETURN is
264
353
// a statement about what the caller expects from a function, whereas a
265
354
// POSTCONDITION is a statement about guarantees made by the function itself.
266
- #define CHECK_RETURN (CONDITION ) INVARIANT(CONDITION, " Check return value" )
355
+ #define CHECK_RETURN1 (CONDITION ) INVARIANT2(CONDITION, " Check return value" )
356
+ #define CHECK_RETURN2 (CONDITION, DIAGNOSTICS ) \
357
+ INVARIANT3 (CONDITION, " Check return value" , DIAGNOSTICS)
358
+
359
+ #define CHECK_RETURN (...) REDIRECT(CHECK_RETURN, __VA_ARGS__)
360
+
267
361
#define CHECK_RETURN_STRUCTURED (CONDITION, TYPENAME, ...) \
268
362
INVARIANT_STRUCTURED (CONDITION, TYPENAME, __VA_ARGS__)
269
363
270
364
// This should be used to mark dead code
271
- #define UNREACHABLE INVARIANT (false , " Unreachable" )
365
+ #define UNREACHABLE INVARIANT2 (false , " Unreachable" )
272
366
#define UNREACHABLE_STRUCTURED (TYPENAME, ...) \
273
367
INVARIANT_STRUCTURED (false , TYPENAME, __VA_ARGS__)
274
368
275
369
// This condition should be used to document that assumptions that are
276
370
// made on goto_functions, goto_programs, exprts, etc. being well formed.
277
371
// "The data structure is corrupt or malformed"
278
- #define DATA_INVARIANT (CONDITION, REASON ) INVARIANT(CONDITION, REASON)
372
+ #define DATA_INVARIANT2 (CONDITION, REASON ) INVARIANT2(CONDITION, REASON)
373
+ #define DATA_INVARIANT3 (CONDITION, REASON, DIAGNOSTICS ) \
374
+ INVARIANT3 (CONDITION, REASON, DIAGNOSTICS)
375
+
376
+ #define DATA_INVARIANT (...) REDIRECT(DATA_INVARIANT, __VA_ARGS__)
377
+
279
378
#define DATA_INVARIANT_STRUCTURED (CONDITION, TYPENAME, ...) \
280
379
INVARIANT_STRUCTURED (CONDITION, TYPENAME, __VA_ARGS__)
281
380
282
381
// Legacy annotations
283
382
284
383
// The following should not be used in new code and are only intended
285
384
// to migrate documentation and "error handling" in older code.
286
- #define TODO INVARIANT (false , " Todo" )
287
- #define UNIMPLEMENTED INVARIANT (false , " Unimplemented" )
288
- #define UNHANDLED_CASE INVARIANT (false , " Unhandled case" )
385
+ #define TODO INVARIANT2 (false , " Todo" )
386
+ #define UNIMPLEMENTED INVARIANT2 (false , " Unimplemented" )
387
+ #define UNHANDLED_CASE INVARIANT2 (false , " Unhandled case" )
289
388
290
389
#endif // CPROVER_UTIL_INVARIANT_H
0 commit comments