CBMC
Loading...
Searching...
No Matches
instrument_contracts.cpp
Go to the documentation of this file.
1/*******************************************************************\
2
3Module: Instrument Contracts
4
5Author: Daniel Kroening, dkr@amazon.com
6
7\*******************************************************************/
8
11
13
14#include <util/c_types.h>
17#include <util/replace_symbol.h>
18#include <util/std_code.h>
19
21
22#include <ansi-c/expr2c.h>
23
24#define MAX_TEXT 20
25
26std::optional<code_with_contract_typet>
27get_contract(const irep_idt &function_identifier, const namespacet &ns)
28{
29 // contracts are in a separate symbol, with prefix "contract::"
30 auto contract_identifier = "contract::" + id2string(function_identifier);
31 const symbolt *symbol_ptr;
33 return {}; // symbol not found
34 else
36}
37
38bool has_contract(const irep_idt &function_identifier, const namespacet &ns)
39{
40 return get_contract(function_identifier, ns).has_value();
41}
42
43static std::string expr2text(const exprt &src, const namespacet &ns)
44{
45 auto text = expr2c(src, ns);
46 if(text.size() >= MAX_TEXT)
47 return std::string(text, 0, MAX_TEXT - 3) + "...";
48 else
49 return text;
50}
51
53{
54 if(src.id() == ID_dereference)
55 {
56 return to_dereference_expr(src).pointer();
57 }
58 else
59 return address_of_exprt(src);
60}
61
62// add the function to the source location
64add_function(irep_idt function_identifier, source_locationt src)
65{
66 if(!src.get_file().empty())
67 src.set_function(function_identifier);
68
69 return src;
70}
71
72// add the function to the source location
73exprt add_function(irep_idt function_identifier, exprt src)
74{
75 for(auto &op : src.operands())
76 op = add_function(function_identifier, op);
77
78 if(!src.source_location().get_file().empty())
79 src.add_source_location().set_function(function_identifier);
80
81 return src;
82}
83
85 exprt src,
86 const source_locationt &source_location)
87{
88 for(auto &op : src.operands())
89 op = replace_source_location(op, source_location);
90
91 src.add_source_location() = source_location;
92
93 return src;
94}
95
96static bool is_symbol_member(const exprt &expr)
97{
98 if(expr.id() == ID_symbol)
99 return true;
100 else if(expr.id() == ID_member)
101 return is_symbol_member(to_member_expr(expr).struct_op());
102 else
103 return false;
104}
105
106exprt assigns_match(const exprt &assigns, const exprt &lhs)
107{
108 if(is_symbol_member(lhs) && assigns == lhs)
109 return true_exprt(); // trivial match
110
111 if(lhs.id() == ID_member)
112 {
113 if(assigns_match(assigns, to_member_expr(lhs).struct_op()).is_true())
114 return true_exprt();
115 }
116 else if(lhs.id() == ID_index)
117 {
118 if(assigns_match(assigns, to_index_expr(lhs).array()).is_true())
119 return true_exprt();
120 }
121
122 auto assigns_address = make_address(assigns);
123 auto lhs_address = make_address(lhs);
124
125 if(lhs.type() == assigns.type())
126 {
128 }
129 else
130 {
131 // need to compare offset ranges
133 return same_object;
134 }
135}
136
138{
139 return src.id() == ID_lambda ? to_lambda_expr(src).where() : src;
140}
141
143 irep_idt function_identifier,
144 const exprt::operandst &assigns,
145 const exprt &lhs)
146{
148
149 for(auto &assigns_clause : assigns)
150 {
152
154 {
155 auto &condition = to_binary_expr(a).op0();
156 auto &targets = to_multi_ary_expr(to_binary_expr(a).op1());
157 for(auto &target : targets.operands())
158 {
159 auto target_address = make_address(target);
160 auto lhs_address = make_address(lhs);
163 disjuncts.push_back(
165 }
166 }
167 else
168 {
169 auto match = assigns_match(a, lhs);
170
171 // trivial?
172 if(match.is_true())
173 return true_exprt();
174
175 disjuncts.push_back(std::move(match));
176 }
177 }
178
179 return disjunction(disjuncts);
180}
181
182static bool
183is_procedure_local(const irep_idt &function_identifier, const exprt &lhs)
184{
185 if(lhs.id() == ID_member)
186 return is_procedure_local(
187 function_identifier, to_member_expr(lhs).struct_op());
188 else if(lhs.id() == ID_index)
189 return is_procedure_local(function_identifier, to_index_expr(lhs).array());
190 else if(lhs.id() == ID_symbol)
191 {
192 const auto &symbol_expr = to_symbol_expr(lhs);
193 return symbol_expr.get_identifier().starts_with(
194 id2string(function_identifier) + "::");
195 }
196 else
197 return false;
198}
199
200static bool is_old(const exprt &lhs)
201{
202 if(lhs.id() == ID_symbol)
203 {
204 const auto &symbol_expr = to_symbol_expr(lhs);
205 return symbol_expr.get_identifier().starts_with("old::");
206 }
207 else
208 return false;
209}
210
212 const exprt &src,
213 const std::string &prefix,
214 std::vector<std::pair<symbol_exprt, exprt>> &old_exprs)
215{
216 for(std::size_t i = 0; i < old_exprs.size(); i++)
217 {
218 if(old_exprs[i].second == src)
219 return old_exprs[i].first;
220 }
221
222 auto index = old_exprs.size();
223 irep_idt identifier = prefix + std::to_string(index);
224 old_exprs.emplace_back(symbol_exprt(identifier, src.type()), src);
225
226 return old_exprs.back().first;
227}
228
230 exprt src,
231 const std::string &prefix,
232 std::vector<std::pair<symbol_exprt, exprt>> &old_exprs)
233{
234 if(src.id() == ID_old)
235 {
236 const auto &old_expr = to_unary_expr(src);
237 return find_old_expr(old_expr.op(), prefix, old_exprs);
238 }
239 else
240 {
241 // rec. call
242 for(auto &op : src.operands())
243 op = replace_old(op, prefix, old_exprs);
244 return src;
245 }
246}
247
249 const std::vector<std::pair<symbol_exprt, exprt>> &old_exprs,
250 const source_locationt &source_location)
251{
252 goto_programt dest;
253
254 for(const auto &old_expr : old_exprs)
255 {
256 auto lhs = old_expr.first;
257 auto fixed_rhs = replace_source_location(old_expr.second, source_location);
259 goto_programt::make_assignment(lhs, fixed_rhs, source_location);
260 dest.add(std::move(assignment_instruction));
261 }
262
263 return dest;
264}
265
267 goto_functionst::function_mapt::value_type &f,
268 const namespacet &ns)
269{
270 // contracts are in a separate symbol, with prefix "contract::"
271 auto contract_identifier = "contract::" + id2string(f.first);
272 const symbolt *symbol_ptr;
274 return; // nothing to check
275
277
278 auto &body = f.second.body;
279
280 if(body.instructions.empty())
281 return; // nothing to check
282
283 // new instructions to add at the beginning of the function
285
286 // precondition?
287 if(!contract.c_requires().empty())
288 {
289 // stick these in as assumptions, preserving the ordering
290 goto_programt dest;
291 for(auto &assumption : contract.c_requires())
292 {
296 fixed_assumption, fixed_assumption.source_location()));
297 }
298 }
299
300 // record "old(...)" expressions.
301 std::vector<std::pair<symbol_exprt, exprt>> old_exprs;
302 const auto old_prefix = "old::" + id2string(f.first);
303
304 // postcondition?
305 if(!contract.c_ensures().empty())
306 {
307 // Stick the postconditions in as assertions at the end
308 auto last = body.instructions.end();
309 if(std::prev(last)->is_end_function())
310 last = std::prev(last);
311
312 for(auto &assertion : contract.c_ensures())
313 {
315
316 std::string comment = "postcondition";
317 if(contract.c_ensures().size() >= 2)
319
320 auto location = assertion.source_location();
321 location.set_function(f.first); // seems to be missing
322 location.set_property_class(ID_postcondition);
323 location.set_comment(comment);
324
325 auto replaced_assertion =
327
329
332
333 body.insert_before_swap(last, assertion_instruction);
334 }
335 }
336
337 // do 'old' in the body
338 if(
339 !contract.c_assigns().empty() || !contract.c_requires().empty() ||
340 !contract.c_ensures().empty())
341 {
342 for(auto &instruction : body.instructions)
343 instruction.transform(
344 [&old_prefix, &old_exprs](exprt expr) -> std::optional<exprt> {
345 return replace_old(expr, old_prefix, old_exprs);
346 });
347 }
348
349 // Add assignments to 'old' symbols at the beginning of the function.
350 {
351 auto tmp =
352 old_assignments(old_exprs, add_function(f.first, symbol_ptr->location));
353 add_at_beginning.destructive_append(tmp);
354 }
355
356 body.destructive_insert(body.instructions.begin(), add_at_beginning);
357
358 // assigns?
359 if(
360 !contract.c_assigns().empty() || !contract.c_requires().empty() ||
361 !contract.c_ensures().empty())
362 {
363 for(auto it = body.instructions.begin(); it != body.instructions.end();
364 it++)
365 {
366 if(it->is_assign())
367 {
368 const auto &lhs = it->assign_lhs();
369
370 // Parameter or local or old? Ignore.
371 if(is_procedure_local(f.first, lhs))
372 continue; // ok
373
374 if(is_old(lhs))
375 continue; // ok
376
377 // maybe not ok
378 auto assigns_assertion =
379 make_assigns_assertion(f.first, contract.c_assigns(), lhs);
380 auto location = it->source_location();
381 location.set_property_class("assigns");
382 location.set_comment("assigns clause");
384 std::move(assigns_assertion), std::move(location));
385 body.insert_before_swap(it, assertion_instruction);
386 it++; // skip over the assertion we have just generated
387 }
388 }
389 }
390}
391
393 goto_functionst::function_mapt::value_type &f,
394 const goto_modelt &goto_model)
395{
396 auto &body = f.second.body;
397 const namespacet ns(goto_model.symbol_table);
398
399 std::size_t call_site_counter = 0;
400
401 for(auto it = body.instructions.begin(); it != body.instructions.end(); it++)
402 {
403 if(it->is_function_call())
404 {
405 const auto &function = it->call_function();
406 if(function.id() == ID_symbol)
407 {
408 const auto &symbol = ns.lookup(to_symbol_expr(function));
409
410 const auto contract_opt = get_contract(symbol.name, ns);
411
412 if(!contract_opt.has_value())
413 continue;
414
415 auto &contract = contract_opt.value();
416
417 // record "old(...)" expressions.
418 std::vector<std::pair<symbol_exprt, exprt>> old_exprs;
419 const auto old_prefix = "old::" + id2string(f.first) + "::call-site-" +
420 std::to_string(++call_site_counter) + "::";
421
422 // need to substitute parameters
423 const auto f_it =
424 goto_model.goto_functions.function_map.find(symbol.name);
425
426 if(f_it == goto_model.goto_functions.function_map.end())
427 DATA_INVARIANT(false, "failed to find function in function_map");
428
429 replace_symbolt replace_symbol;
430 const auto &parameters = to_code_type(symbol.type).parameters();
431 const auto &arguments = it->call_arguments();
432
433 for(std::size_t p = 0; p < f_it->second.parameter_identifiers.size();
434 p++)
435 {
436 auto p_symbol = symbol_exprt(
437 f_it->second.parameter_identifiers[p], parameters[p].type());
438 replace_symbol.insert(p_symbol, arguments[p]);
439 }
440
441 // replace __CPROVER_return_value by the lhs of the call
442 const auto &call_lhs = it->call_lhs();
443 replace_symbol.insert(
444 symbol_exprt(CPROVER_PREFIX "return_value", call_lhs.type()),
445 call_lhs);
446
447 goto_programt dest;
448
449 // assert the preconditions
450 for(auto &precondition : contract.c_requires())
451 {
454
455 auto location = it->source_location();
456 location.set_property_class(ID_precondition);
457 location.set_comment(
458 id2string(symbol.display_name()) + " precondition " +
460
462 replace_symbol(replaced_precondition);
463
464 dest.add(
466 }
467
468 // havoc the 'assigned' variables
469 for(auto &assigns_clause_lambda : contract.c_assigns())
470 {
471 auto location = it->source_location();
472
473 auto assigns_clause =
475
477 {
478 const auto &condition = to_binary_expr(assigns_clause).op0();
479 auto replaced_condition = condition;
480 replace_symbol(replaced_condition);
481
482 const auto &targets =
484 .operands();
485
486 for(auto &target : targets)
487 {
488 auto rhs = side_effect_expr_nondett(target.type(), location);
489
490 auto replaced_lhs = target;
491 replace_symbol(replaced_lhs);
492
493 auto goto_instruction =
495 not_exprt(replaced_condition), location));
496
497 dest.add(
499
500 auto skip_instruction =
501 dest.add(goto_programt::make_skip(location));
502
503 goto_instruction->complete_goto(skip_instruction);
504 }
505 }
506 else
507 {
508 const auto &lhs = assigns_clause;
509 auto rhs = side_effect_expr_nondett(lhs.type(), location);
510
511 auto replaced_lhs = lhs;
512 replace_symbol(replaced_lhs);
514
515 dest.add(goto_programt::make_assignment(fixed_lhs, rhs, location));
516 }
517 }
518
519 // assume the postconditions
520 for(auto &postcondition : contract.c_ensures())
521 {
522 auto &location = it->source_location();
523
526 replace_symbol(replaced_postcondition1);
527
530
531 dest.add(
533 }
534
535 // now insert the assignents to old::... at the beginning
536 // of 'dest'
537 {
538 auto tmp = old_assignments(old_exprs, it->source_location());
539 dest.destructive_insert(dest.instructions.begin(), tmp);
540 }
541
542 // remove the function call
543 it->turn_into_skip();
544
545 // insert after 'it' to preserve branches to the call
546 body.destructive_insert(std::next(it), dest);
547 }
548 }
549 }
550}
551
553{
554 const namespacet ns(goto_model.symbol_table);
555
556 for(auto &f : goto_model.goto_functions.function_map)
557 {
560 }
561}
const code_with_contract_typet & to_code_with_contract_type(const typet &type)
Cast a typet to a code_with_contract_typet.
Definition c_types.h:467
Operator to return the address of an object.
ait supplies three of the four components needed: an abstract interpreter (in this case handling func...
Definition ai.h:562
Boolean AND.
Definition std_expr.h:2125
dstringt has one field, an unsigned integer no which is an index into a static table of strings.
Definition dstring.h:38
Equality.
Definition std_expr.h:1366
Base class for all expressions.
Definition expr.h:56
std::vector< exprt > operandst
Definition expr.h:58
typet & type()
Return the type of the expression.
Definition expr.h:84
operandst & operands()
Definition expr.h:94
const source_locationt & source_location() const
Definition expr.h:231
source_locationt & add_source_location()
Definition expr.h:236
function_mapt function_map
symbol_tablet symbol_table
Symbol table.
Definition goto_model.h:31
goto_functionst goto_functions
GOTO functions.
Definition goto_model.h:34
A generic container class for the GOTO intermediate representation of one function.
static instructiont make_assumption(const exprt &g, const source_locationt &l=source_locationt::nil())
instructionst instructions
The list of instructions in the goto program.
void destructive_insert(const_targett target, goto_programt &p)
Inserts the given program p before target.
static instructiont make_assignment(const code_assignt &_code, const source_locationt &l=source_locationt::nil())
Create an assignment instruction.
static instructiont make_skip(const source_locationt &l=source_locationt::nil())
targett add(instructiont &&instruction)
Adds a given instruction at the end.
static instructiont make_assertion(const exprt &g, const source_locationt &l=source_locationt::nil())
static instructiont make_incomplete_goto(const exprt &_cond, const source_locationt &l=source_locationt::nil())
const irep_idt & id() const
Definition irep.h:388
A namespacet is essentially one or two symbol tables bound together, to allow for symbol lookups in t...
Definition namespace.h:91
bool lookup(const irep_idt &name, const symbolt *&symbol) const override
See documentation for namespace_baset::lookup().
Boolean negation.
Definition std_expr.h:2454
Replace a symbol expression by a given expression.
void insert(const class symbol_exprt &old_expr, const exprt &new_expr)
Sets old_expr to be replaced by new_expr if we don't already have a replacement; otherwise does nothi...
A side_effect_exprt that returns a non-deterministically chosen value.
Definition std_code.h:1520
const irep_idt & get_file() const
void set_function(const irep_idt &function)
Expression to hold a symbol (variable)
Definition std_expr.h:131
Symbol table entry.
Definition symbol.h:28
The Boolean constant true.
Definition std_expr.h:3190
static exprt conditional_cast(const exprt &expr, const typet &type)
Definition std_expr.h:2081
#define CPROVER_PREFIX
std::string expr2c(const exprt &expr, const namespacet &ns, const expr2c_configurationt &configuration)
Definition expr2c.cpp:4155
Symbol Table + CFG.
static std::string expr2text(const exprt &src, const namespacet &ns)
static bool is_symbol_member(const exprt &expr)
static bool is_procedure_local(const irep_idt &function_identifier, const exprt &lhs)
void replace_function_calls_by_contracts(goto_functionst::function_mapt::value_type &f, const goto_modelt &goto_model)
goto_programt old_assignments(const std::vector< std::pair< symbol_exprt, exprt > > &old_exprs, const source_locationt &source_location)
static bool is_old(const exprt &lhs)
static exprt make_address(exprt src)
exprt replace_old(exprt src, const std::string &prefix, std::vector< std::pair< symbol_exprt, exprt > > &old_exprs)
static exprt instantiate_contract_lambda(exprt src)
std::optional< code_with_contract_typet > get_contract(const irep_idt &function_identifier, const namespacet &ns)
source_locationt add_function(irep_idt function_identifier, source_locationt src)
exprt assigns_match(const exprt &assigns, const exprt &lhs)
#define MAX_TEXT
void instrument_contracts(goto_modelt &goto_model)
static exprt make_assigns_assertion(irep_idt function_identifier, const exprt::operandst &assigns, const exprt &lhs)
void instrument_contract_checks(goto_functionst::function_mapt::value_type &f, const namespacet &ns)
symbol_exprt find_old_expr(const exprt &src, const std::string &prefix, std::vector< std::pair< symbol_exprt, exprt > > &old_exprs)
bool has_contract(const irep_idt &function_identifier, const namespacet &ns)
exprt replace_source_location(exprt src, const source_locationt &source_location)
Instrument Given Invariants.
const std::string & id2string(const irep_idt &d)
Definition irep.h:44
bool is_true(const literalt &l)
Definition literal.h:198
API to expression classes for 'mathematical' expressions.
const lambda_exprt & to_lambda_expr(const exprt &expr)
Cast an exprt to a lambda_exprt.
const dereference_exprt & to_dereference_expr(const exprt &expr)
Cast an exprt to a dereference_exprt.
exprt same_object(const exprt &p1, const exprt &p2)
Various predicates over pointers in programs.
void postcondition(const namespacet &ns, const value_sett &value_set, const symex_target_equationt &equation, const goto_symex_statet &s, exprt &dest)
void precondition(const namespacet &ns, value_setst &value_sets, const goto_programt::const_targett target, const symex_target_equationt &equation, const goto_symex_statet &s, exprt &dest, message_handlert &message_handler)
static std::string comment(const rw_set_baset::entryt &entry, bool write)
#define DATA_INVARIANT(CONDITION, REASON)
This condition should be used to document that assumptions that are made on goto_functions,...
Definition invariant.h:534
exprt disjunction(const exprt::operandst &op)
1) generates a disjunction for two or more operands 2) for one operand, returns the operand 3) return...
Definition std_expr.cpp:71
const index_exprt & to_index_expr(const exprt &expr)
Cast an exprt to an index_exprt.
Definition std_expr.h:1538
const binary_exprt & to_binary_expr(const exprt &expr)
Cast an exprt to a binary_exprt.
Definition std_expr.h:715
const unary_exprt & to_unary_expr(const exprt &expr)
Cast an exprt to a unary_exprt.
Definition std_expr.h:426
const multi_ary_exprt & to_multi_ary_expr(const exprt &expr)
Cast an exprt to a multi_ary_exprt.
Definition std_expr.h:987
const member_exprt & to_member_expr(const exprt &expr)
Cast an exprt to a member_exprt.
Definition std_expr.h:3063
const symbol_exprt & to_symbol_expr(const exprt &expr)
Cast an exprt to a symbol_exprt.
Definition std_expr.h:272
const code_typet & to_code_type(const typet &type)
Cast a typet to a code_typet.
Definition std_types.h:788