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,54 @@ invariant_violated_string(
201
249
#define __this_function__ __func__
202
250
#endif
203
251
204
- #define INVARIANT (CONDITION, REASON ) \
252
+ // We wrap macros that take __VA_ARGS__ as an argument with EXPAND_MACRO(). This
253
+ // is to account for the behaviour of msvc, which otherwise would not expand
254
+ // __VA_ARGS__ to multiple arguments in the outermost macro invocation.
255
+ #define EXPAND_MACRO (x ) x
256
+
257
+ #define GET_MACRO (X1, X2, X3, X4, X5, X6, MACRO, ...) MACRO
258
+
259
+ // This macro dispatches to the macros MACRO<n> (with 1 <= n <= 6) and calls
260
+ // them with the arguments in __VA_ARGS__. The invocation of GET_MACRO() selects
261
+ // MACRO<n> when __VA_ARGS__ consists of n arguments.
262
+ #define REDIRECT (MACRO, ...) \
263
+ do \
264
+ { \
265
+ EXPAND_MACRO ( \
266
+ GET_MACRO ( \
267
+ __VA_ARGS__, \
268
+ MACRO##6 , \
269
+ MACRO##5 , \
270
+ MACRO##4 , \
271
+ MACRO##3 , \
272
+ MACRO##2 , \
273
+ MACRO##1 , \
274
+ DUMMY_MACRO_ARG)(__VA_ARGS__)); \
275
+ } while (false )
276
+
277
+ #define INVARIANT2 (CONDITION, REASON ) \
205
278
do /* NOLINT */ \
206
279
{ \
207
280
if (!(CONDITION)) \
208
281
invariant_violated_string ( \
209
282
__FILE__, \
210
283
__this_function__, \
211
284
__LINE__, \
285
+ #CONDITION, \
286
+ (REASON)); /* NOLINT */ \
287
+ } while (false )
288
+
289
+ #define INVARIANT3 (CONDITION, REASON, DIAGNOSTICS ) \
290
+ do /* NOLINT */ \
291
+ { \
292
+ if (!(CONDITION)) \
293
+ invariant_violated_structured<invariant_with_diagnostics_failedt>( \
294
+ __FILE__, \
295
+ __this_function__, \
296
+ __LINE__, \
297
+ #CONDITION, \
212
298
(REASON), \
213
- #CONDITION) ; /* NOLINT */ \
299
+ (DIAGNOSTICS)) ; /* NOLINT */ \
214
300
} while (false )
215
301
216
302
#define INVARIANT_STRUCTURED (CONDITION, TYPENAME, ...) \
@@ -227,21 +313,27 @@ invariant_violated_string(
227
313
228
314
#endif // End CPROVER_DO_NOT_CHECK / CPROVER_ASSERT / ... if block
229
315
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.
316
+ // Short hand macros. The variants *_STRUCTURED below allow to specify a custom
317
+ // exception, and are equivalent to INVARIANT_STRUCTURED.
233
318
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.
319
+ #define INVARIANT (...) EXPAND_MACRO(REDIRECT(INVARIANT, __VA_ARGS__))
320
+
321
+ // The condition should only contain (unmodified) inputs to the method. Inputs
322
+ // include arguments passed to the function, as well as member variables that
323
+ // the function may read.
237
324
// "The design of the system means that the arguments to this method
238
325
// will always meet this condition".
239
326
//
240
327
// The PRECONDITION documents / checks assumptions about the inputs
241
328
// that is the caller's responsibility to make happen before the call.
242
- #define PRECONDITION (CONDITION ) INVARIANT(CONDITION, " Precondition" )
243
- #define PRECONDITION_STRUCTURED (CONDITION, TYPENAME, ...) \
244
- INVARIANT_STRUCTURED (CONDITION, TYPENAME, __VA_ARGS__)
329
+ #define PRECONDITION1 (CONDITION ) INVARIANT2(CONDITION, " Precondition" )
330
+ #define PRECONDITION2 (CONDITION, DIAGNOSTICS ) \
331
+ INVARIANT3 (CONDITION, " Precondition" , DIAGNOSTICS)
332
+
333
+ #define PRECONDITION (...) EXPAND_MACRO(REDIRECT(PRECONDITION, __VA_ARGS__))
334
+
335
+ #define PRECONDITION_STRUCTURED (CONDITION, TYPENAME, ...) \
336
+ EXPAND_MACRO (INVARIANT_STRUCTURED(CONDITION, TYPENAME, __VA_ARGS__))
245
337
246
338
// The condition should only contain variables that will be returned /
247
339
// output without further modification.
@@ -251,9 +343,14 @@ invariant_violated_string(
251
343
// output when it returns, given that it was called with valid inputs.
252
344
// Outputs include the return value of the function, as well as member
253
345
// variables that the function may write.
254
- #define POSTCONDITION (CONDITION ) INVARIANT(CONDITION, " Postcondition" )
255
- #define POSTCONDITION_STRUCTURED (CONDITION, TYPENAME, ...) \
256
- INVARIANT_STRUCTURED (CONDITION, TYPENAME, __VA_ARGS__)
346
+ #define POSTCONDITION1 (CONDITION ) INVARIANT2(CONDITION, " Postcondition" )
347
+ #define POSTCONDITION2 (CONDITION, DIAGNOSTICS ) \
348
+ INVARIANT3 (CONDITION, " Postcondition" , DIAGNOSTICS)
349
+
350
+ #define POSTCONDITION (...) EXPAND_MACRO(REDIRECT(POSTCONDITION, __VA_ARGS__))
351
+
352
+ #define POSTCONDITION_STRUCTURED (CONDITION, TYPENAME, ...) \
353
+ EXPAND_MACRO (INVARIANT_STRUCTURED(CONDITION, TYPENAME, __VA_ARGS__))
257
354
258
355
// The condition should only contain (unmodified) values that were
259
356
// changed by a previous method call.
@@ -263,28 +360,38 @@ invariant_violated_string(
263
360
// A difference between CHECK_RETURN and POSTCONDITION is that CHECK_RETURN is
264
361
// a statement about what the caller expects from a function, whereas a
265
362
// POSTCONDITION is a statement about guarantees made by the function itself.
266
- #define CHECK_RETURN (CONDITION ) INVARIANT(CONDITION, " Check return value" )
267
- #define CHECK_RETURN_STRUCTURED (CONDITION, TYPENAME, ...) \
268
- INVARIANT_STRUCTURED (CONDITION, TYPENAME, __VA_ARGS__)
363
+ #define CHECK_RETURN1 (CONDITION ) INVARIANT2(CONDITION, " Check return value" )
364
+ #define CHECK_RETURN2 (CONDITION, DIAGNOSTICS ) \
365
+ INVARIANT3 (CONDITION, " Check return value" , DIAGNOSTICS)
366
+
367
+ #define CHECK_RETURN (...) EXPAND_MACRO(REDIRECT(CHECK_RETURN, __VA_ARGS__))
368
+
369
+ #define CHECK_RETURN_STRUCTURED (CONDITION, TYPENAME, ...) \
370
+ EXPAND_MACRO (INVARIANT_STRUCTURED(CONDITION, TYPENAME, __VA_ARGS__))
269
371
270
372
// This should be used to mark dead code
271
- #define UNREACHABLE INVARIANT (false , " Unreachable" )
272
- #define UNREACHABLE_STRUCTURED (TYPENAME, ...) \
273
- INVARIANT_STRUCTURED (false , TYPENAME, __VA_ARGS__)
373
+ #define UNREACHABLE INVARIANT2 (false , " Unreachable" )
374
+ #define UNREACHABLE_STRUCTURED (TYPENAME, ...) \
375
+ EXPAND_MACRO ( INVARIANT_STRUCTURED(false , TYPENAME, __VA_ARGS__) )
274
376
275
377
// This condition should be used to document that assumptions that are
276
378
// made on goto_functions, goto_programs, exprts, etc. being well formed.
277
379
// "The data structure is corrupt or malformed"
278
- #define DATA_INVARIANT (CONDITION, REASON ) INVARIANT(CONDITION, REASON)
279
- #define DATA_INVARIANT_STRUCTURED (CONDITION, TYPENAME, ...) \
280
- INVARIANT_STRUCTURED (CONDITION, TYPENAME, __VA_ARGS__)
380
+ #define DATA_INVARIANT2 (CONDITION, REASON ) INVARIANT2(CONDITION, REASON)
381
+ #define DATA_INVARIANT3 (CONDITION, REASON, DIAGNOSTICS ) \
382
+ INVARIANT3 (CONDITION, REASON, DIAGNOSTICS)
383
+
384
+ #define DATA_INVARIANT (...) EXPAND_MACRO(REDIRECT(DATA_INVARIANT, __VA_ARGS__))
385
+
386
+ #define DATA_INVARIANT_STRUCTURED (CONDITION, TYPENAME, ...) \
387
+ EXPAND_MACRO (INVARIANT_STRUCTURED(CONDITION, TYPENAME, __VA_ARGS__))
281
388
282
389
// Legacy annotations
283
390
284
391
// The following should not be used in new code and are only intended
285
392
// 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" )
393
+ #define TODO INVARIANT2 (false , " Todo" )
394
+ #define UNIMPLEMENTED INVARIANT2 (false , " Unimplemented" )
395
+ #define UNHANDLED_CASE INVARIANT2 (false , " Unhandled case" )
289
396
290
397
#endif // CPROVER_UTIL_INVARIANT_H
0 commit comments