@@ -9,10 +9,11 @@ Author: Martin Brain, martin.brain@diffblue.com
99#ifndef CPROVER_UTIL_INVARIANT_H
1010#define CPROVER_UTIL_INVARIANT_H
1111
12+ #include < cstdlib>
1213#include < stdexcept>
13- #include < type_traits>
1414#include < string>
15- #include < cstdlib>
15+ #include < tuple>
16+ #include < type_traits>
1617
1718/*
1819** Invariants document conditions that the programmer believes to
@@ -83,7 +84,7 @@ class invariant_failedt
8384 const std::string reason;
8485
8586public:
86- std::string what () const noexcept ;
87+ virtual std::string what () const noexcept ;
8788
8889 invariant_failedt (
8990 const std::string &_file,
@@ -102,9 +103,47 @@ class invariant_failedt
102103 }
103104};
104105
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+
105142#if defined(CPROVER_INVARIANT_CPROVER_ASSERT)
106143// 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 ) \
108147 __CPROVER_assert ((CONDITION), "Invariant : " REASON)
109148
110149#define INVARIANT_STRUCTURED (CONDITION, TYPENAME, ...) \
@@ -115,14 +154,23 @@ class invariant_failedt
115154// This is *not* recommended as it can result in unpredictable behaviour
116155// including silently reporting incorrect results.
117156// 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 )
119165#define INVARIANT_STRUCTURED (CONDITION, TYPENAME, ...) do {} while (false )
120166
121167#elif defined(CPROVER_INVARIANT_ASSERT)
122168// Not recommended but provided for backwards compatability
123169#include < cassert>
124170// 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 */
126174// NOLINTNEXTLINE(*)
127175#define INVARIANT_STRUCTURED (CONDITION, TYPENAME, ...) assert ((CONDITION))
128176#else
@@ -185,8 +233,8 @@ invariant_violated_string(
185233 const std::string &file,
186234 const std::string &function,
187235 const int line,
188- const std::string &reason ,
189- const std::string &condition )
236+ const std::string &condition ,
237+ const std::string &reason )
190238{
191239 invariant_violated_structured<invariant_failedt>(
192240 file, function, line, condition, reason);
@@ -201,16 +249,46 @@ invariant_violated_string(
201249#define __this_function__ __func__
202250#endif
203251
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 ) \
205270 do /* NOLINT */ \
206271 { \
207272 if (!(CONDITION)) \
208273 invariant_violated_string ( \
209274 __FILE__, \
210275 __this_function__, \
211276 __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, \
212290 (REASON), \
213- #CONDITION) ; /* NOLINT */ \
291+ (DIAGNOSTICS)) ; /* NOLINT */ \
214292 } while (false )
215293
216294#define INVARIANT_STRUCTURED (CONDITION, TYPENAME, ...) \
@@ -227,19 +305,25 @@ invariant_violated_string(
227305
228306#endif // End CPROVER_DO_NOT_CHECK / CPROVER_ASSERT / ... if block
229307
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__)
233312
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.
237316// "The design of the system means that the arguments to this method
238317// will always meet this condition".
239318//
240319// The PRECONDITION documents / checks assumptions about the inputs
241320// 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+
243327#define PRECONDITION_STRUCTURED (CONDITION, TYPENAME, ...) \
244328 INVARIANT_STRUCTURED (CONDITION, TYPENAME, __VA_ARGS__)
245329
@@ -251,7 +335,12 @@ invariant_violated_string(
251335// output when it returns, given that it was called with valid inputs.
252336// Outputs include the return value of the function, as well as member
253337// 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+
255344#define POSTCONDITION_STRUCTURED (CONDITION, TYPENAME, ...) \
256345 INVARIANT_STRUCTURED (CONDITION, TYPENAME, __VA_ARGS__)
257346
@@ -263,28 +352,38 @@ invariant_violated_string(
263352// A difference between CHECK_RETURN and POSTCONDITION is that CHECK_RETURN is
264353// a statement about what the caller expects from a function, whereas a
265354// 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+
267361#define CHECK_RETURN_STRUCTURED (CONDITION, TYPENAME, ...) \
268362 INVARIANT_STRUCTURED (CONDITION, TYPENAME, __VA_ARGS__)
269363
270364// This should be used to mark dead code
271- #define UNREACHABLE INVARIANT (false , " Unreachable" )
365+ #define UNREACHABLE INVARIANT2 (false , " Unreachable" )
272366#define UNREACHABLE_STRUCTURED (TYPENAME, ...) \
273367 INVARIANT_STRUCTURED (false , TYPENAME, __VA_ARGS__)
274368
275369// This condition should be used to document that assumptions that are
276370// made on goto_functions, goto_programs, exprts, etc. being well formed.
277371// "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+
279378#define DATA_INVARIANT_STRUCTURED (CONDITION, TYPENAME, ...) \
280379 INVARIANT_STRUCTURED (CONDITION, TYPENAME, __VA_ARGS__)
281380
282381// Legacy annotations
283382
284383// The following should not be used in new code and are only intended
285384// 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" )
289388
290389#endif // CPROVER_UTIL_INVARIANT_H
0 commit comments