CBMC
instrument_contracts.cpp
Go to the documentation of this file.
1 /*******************************************************************\
2 
3 Module: Instrument Contracts
4 
5 Author: Daniel Kroening, dkr@amazon.com
6 
7 \*******************************************************************/
8 
11 
12 #include "instrument_contracts.h"
13 
14 #include <util/c_types.h>
15 #include <util/mathematical_expr.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 
26 std::optional<code_with_contract_typet>
27 get_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;
32  if(ns.lookup(contract_identifier, symbol_ptr))
33  return {}; // symbol not found
34  else
35  return to_code_with_contract_type(symbol_ptr->type);
36 }
37 
38 bool has_contract(const irep_idt &function_identifier, const namespacet &ns)
39 {
40  return get_contract(function_identifier, ns).has_value();
41 }
42 
43 static 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
64 add_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
73 exprt 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 
96 static 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 
106 exprt 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  {
127  return equal_exprt(assigns_address, lhs_address);
128  }
129  else
130  {
131  // need to compare offset ranges
132  auto same_object = ::same_object(assigns_address, lhs_address);
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 {
147  exprt::operandst disjuncts;
148 
149  for(auto &assigns_clause : assigns)
150  {
151  auto a = instantiate_contract_lambda(assigns_clause);
152 
153  if(a.id() == ID_conditional_target_group)
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);
161  lhs_address =
162  typecast_exprt::conditional_cast(lhs_address, target_address.type());
163  disjuncts.push_back(
164  and_exprt(condition, equal_exprt(target_address, lhs_address)));
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 
182 static bool
183 is_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 
200 static 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);
258  auto assignment_instruction =
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;
273  if(ns.lookup(contract_identifier, symbol_ptr))
274  return; // nothing to check
275 
276  auto &contract = to_code_with_contract_type(symbol_ptr->type);
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
284  goto_programt add_at_beginning;
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  {
293  exprt assumption_instance = instantiate_contract_lambda(assumption);
294  auto fixed_assumption = add_function(f.first, assumption_instance);
295  add_at_beginning.add(goto_programt::make_assumption(
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  {
314  exprt assertion_instance = instantiate_contract_lambda(assertion);
315 
316  std::string comment = "postcondition";
317  if(contract.c_ensures().size() >= 2)
318  comment += " " + expr2text(assertion_instance, ns);
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 =
326  replace_old(assertion_instance, old_prefix, old_exprs);
327 
328  auto fixed_assertion = add_function(f.first, replaced_assertion);
329 
330  auto assertion_instruction =
331  goto_programt::make_assertion(fixed_assertion, std::move(location));
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");
383  auto assertion_instruction = goto_programt::make_assertion(
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  {
452  auto instantiated_precondition =
454 
455  auto location = it->source_location();
456  location.set_property_class(ID_precondition);
457  location.set_comment(
458  id2string(symbol.display_name()) + " precondition " +
459  expr2text(instantiated_precondition, ns));
460 
461  auto replaced_precondition = instantiated_precondition;
462  replace_symbol(replaced_precondition);
463 
464  dest.add(
465  goto_programt::make_assertion(replaced_precondition, location));
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 =
474  instantiate_contract_lambda(assigns_clause_lambda);
475 
476  if(assigns_clause.id() == ID_conditional_target_group)
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 =
483  to_multi_ary_expr(to_binary_expr(assigns_clause).op1())
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(
498  goto_programt::make_assignment(replaced_lhs, rhs, location));
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);
513  auto fixed_lhs = replace_source_location(replaced_lhs, location);
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 
524  auto replaced_postcondition1 =
526  replace_symbol(replaced_postcondition1);
527 
528  auto replaced_postcondition2 =
529  replace_old(replaced_postcondition1, old_prefix, old_exprs);
530 
531  dest.add(
532  goto_programt::make_assumption(replaced_postcondition2, location));
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.
Definition: pointer_expr.h:540
Boolean AND.
Definition: std_expr.h:2125
exprt & op0()
Definition: expr.h:133
exprt & where()
Definition: std_expr.h:3148
const parameterst & parameters() const
Definition: std_types.h:699
dstringt has one field, an unsigned integer no which is an index into a static table of strings.
Definition: dstring.h:38
bool empty() const
Definition: dstring.h:89
Equality.
Definition: std_expr.h:1366
Base class for all expressions.
Definition: expr.h:56
std::vector< exprt > operandst
Definition: expr.h:58
source_locationt & add_source_location()
Definition: expr.h:236
const source_locationt & source_location() const
Definition: expr.h:231
typet & type()
Return the type of the expression.
Definition: expr.h:84
operandst & operands()
Definition: expr.h:94
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.
Definition: goto_program.h:73
static instructiont make_assumption(const exprt &g, const source_locationt &l=source_locationt::nil())
Definition: goto_program.h:945
instructionst instructions
The list of instructions in the goto program.
Definition: goto_program.h:622
void destructive_insert(const_targett target, goto_programt &p)
Inserts the given program p before target.
Definition: goto_program.h:730
void destructive_append(goto_programt &p)
Appends the given program p to *this. p is destroyed.
Definition: goto_program.h:722
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())
Definition: goto_program.h:891
targett add(instructiont &&instruction)
Adds a given instruction at the end.
Definition: goto_program.h:739
static instructiont make_assertion(const exprt &g, const source_locationt &l=source_locationt::nil())
Definition: goto_program.h:933
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:94
bool lookup(const irep_idt &name, const symbolt *&symbol) const override
See documentation for namespace_baset::lookup().
Definition: namespace.cpp:148
Boolean negation.
Definition: std_expr.h:2337
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
source_locationt location
Source code location of definition of symbol.
Definition: symbol.h:37
typet type
Type of symbol.
Definition: symbol.h:31
The Boolean constant true.
Definition: std_expr.h:3073
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)
symbol_exprt find_old_expr(const exprt &src, const std::string &prefix, std::vector< std::pair< symbol_exprt, exprt >> &old_exprs)
void replace_function_calls_by_contracts(goto_functionst::function_mapt::value_type &f, const goto_modelt &goto_model)
static bool is_old(const exprt &lhs)
static exprt make_address(exprt src)
static exprt instantiate_contract_lambda(exprt src)
goto_programt old_assignments(const std::vector< std::pair< symbol_exprt, exprt >> &old_exprs, const source_locationt &source_location)
source_locationt add_function(irep_idt function_identifier, source_locationt src)
exprt assigns_match(const exprt &assigns, const exprt &lhs)
#define MAX_TEXT
exprt replace_old(exprt src, const std::string &prefix, std::vector< std::pair< symbol_exprt, exprt >> &old_exprs)
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)
bool has_contract(const irep_idt &function_identifier, const namespacet &ns)
exprt replace_source_location(exprt src, const source_locationt &source_location)
std::optional< code_with_contract_typet > get_contract(const irep_idt &function_identifier, const namespacet &ns)
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.
Definition: pointer_expr.h:890
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)
Definition: race_check.cpp:108
#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 symbol_exprt & to_symbol_expr(const exprt &expr)
Cast an exprt to a symbol_exprt.
Definition: std_expr.h:272
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 binary_exprt & to_binary_expr(const exprt &expr)
Cast an exprt to a binary_exprt.
Definition: std_expr.h:715
const member_exprt & to_member_expr(const exprt &expr)
Cast an exprt to a member_exprt.
Definition: std_expr.h:2946
const unary_exprt & to_unary_expr(const exprt &expr)
Cast an exprt to a unary_exprt.
Definition: std_expr.h:426
const index_exprt & to_index_expr(const exprt &expr)
Cast an exprt to an index_exprt.
Definition: std_expr.h:1538
const code_typet & to_code_type(const typet &type)
Cast a typet to a code_typet.
Definition: std_types.h:788
std::string to_string(const string_not_contains_constraintt &expr)
Used for debug printing.