CBMC
full_array_abstract_object.cpp
Go to the documentation of this file.
1 /*******************************************************************\
2 
3  Module: Variable Sensitivity
4 
5  Author: Thomas Kiley, thomas.kiley@diffblue.com
6 
7 \*******************************************************************/
8 
9 #include <ostream>
10 
13 #include <util/arith_tools.h>
15 #include <util/std_expr.h>
16 
17 #include "abstract_value_object.h"
20 #include "map_visit.h"
21 
23 {
24  bool is_good;
26  bool overrun;
27 };
28 
30  const exprt &expr,
31  const abstract_environmentt &env,
32  const namespacet &ns);
34  const mp_integer &index,
35  const abstract_environmentt &env,
36  const namespacet &ns);
37 
38 template <typename index_fn>
40  const abstract_environmentt &environment,
41  const exprt &expr,
42  const namespacet &ns,
43  index_fn &fn)
44 {
45  const index_exprt &index_expr = to_index_expr(expr);
46  auto evaluated_index = environment.eval(index_expr.index(), ns);
47  auto index_range = (std::dynamic_pointer_cast<const abstract_value_objectt>(
48  evaluated_index->unwrap_context()))
49  ->index_range(ns);
50 
51  sharing_ptrt<abstract_objectt> result = nullptr;
52  for(const auto &index : index_range)
53  {
54  auto at_index = fn(index_exprt(index_expr.array(), index));
55 
56  result =
57  (result == nullptr)
58  ? at_index
59  : abstract_objectt::merge(result, at_index, widen_modet::no).object;
60 
61  if(result->is_top()) // no point in continuing once we've gone top
62  break;
63  }
64  return result;
65 }
66 
68  typet type,
69  bool top,
70  bool bottom)
71  : abstract_aggregate_baset(type, top, bottom)
72 {
73  DATA_INVARIANT(verify(), "Structural invariants maintained");
74 }
75 
77  const exprt &expr,
78  const abstract_environmentt &environment,
79  const namespacet &ns)
80  : abstract_aggregate_baset(expr, environment, ns)
81 {
82  if(expr.id() == ID_array)
83  {
84  mp_integer i = 0;
85  for(const exprt &entry : expr.operands())
86  {
87  auto index = eval_index(i, environment, ns);
88  map_put(index.value, environment.eval(entry, ns), index.overrun);
89  ++i;
90  }
91  set_not_top();
92  }
93  DATA_INVARIANT(verify(), "Structural invariants maintained");
94 }
95 
97 {
98  // Either the object is top or bottom (=> map empty)
99  // or the map is not empty => neither top nor bottom
101  (is_top() || is_bottom()) == map.empty();
102 }
103 
105 {
106  // A structural invariant of constant_array_abstract_objectt is that
107  // (is_top() || is_bottom()) => map.empty()
108  map.clear();
109 }
110 
112  const abstract_object_pointert &other,
113  const widen_modet &widen_mode) const
114 {
115  auto cast_other =
116  std::dynamic_pointer_cast<const full_array_abstract_objectt>(other);
117  if(cast_other)
118  return full_array_merge(cast_other, widen_mode);
119 
120  return abstract_aggregate_baset::merge(other, widen_mode);
121 }
122 
124  const full_array_pointert &other,
125  const widen_modet &widen_mode) const
126 {
127  if(is_bottom())
128  return std::make_shared<full_array_abstract_objectt>(*other);
129 
130  const auto &result =
131  std::dynamic_pointer_cast<full_array_abstract_objectt>(mutable_clone());
132 
133  bool modified = merge_shared_maps(map, other->map, result->map, widen_mode);
134  if(!modified)
135  {
136  DATA_INVARIANT(verify(), "Structural invariants maintained");
137  return shared_from_this();
138  }
139  else
140  {
141  INVARIANT(!result->is_top(), "Merge of maps will not generate top");
142  INVARIANT(!result->is_bottom(), "Merge of maps will not generate bottom");
143  DATA_INVARIANT(result->verify(), "Structural invariants maintained");
144  return result;
145  }
146 }
147 
149  std::ostream &out,
150  const ai_baset &ai,
151  const namespacet &ns) const
152 {
153  if(is_top() || is_bottom())
154  {
156  }
157  else
158  {
159  out << "{";
160  for(const auto &entry : map.get_sorted_view())
161  {
162  out << "[" << entry.first << "] = ";
163  entry.second->output(out, ai, ns);
164  out << "\n";
165  }
166  out << "}";
167  }
168 }
169 
171  const abstract_environmentt &environment,
172  const exprt &expr,
173  const namespacet &ns) const
174 {
175  if(is_top())
176  return environment.abstract_object_factory(expr.type(), ns, true, false);
177 
178  auto read_element_fn =
179  [this, &environment, &ns](const index_exprt &index_expr) {
180  return this->read_element(environment, index_expr, ns);
181  };
182 
183  auto result = apply_to_index_range(environment, expr, ns, read_element_fn);
184 
185  return (result != nullptr) ? result : get_top_entry(environment, ns);
186 }
187 
189  abstract_environmentt &environment,
190  const namespacet &ns,
191  const std::stack<exprt> &stack,
192  const exprt &expr,
193  const abstract_object_pointert &value,
194  bool merging_write) const
195 {
196  auto write_element_fn =
197  [this, &environment, &ns, &stack, &value, &merging_write](
198  const index_exprt &index_expr) {
199  return this->write_element(
200  environment, ns, stack, index_expr, value, merging_write);
201  };
202 
203  auto result = apply_to_index_range(environment, expr, ns, write_element_fn);
204 
205  return (result != nullptr) ? result : make_top();
206 }
207 
209  const abstract_environmentt &env,
210  const exprt &expr,
211  const namespacet &ns) const
212 {
214  auto index = eval_index(expr, env, ns);
215 
216  if(index.is_good)
217  return map_find_or_top(index.value, env, ns);
218 
219  // Although we don't know where we are reading from, and therefore
220  // we should be returning a TOP value, we may still have useful
221  // additional information in elements of the array - such as write
222  // histories so we merge all the known array elements with TOP and return
223  // that.
224 
225  // Create a new TOP value of the appropriate element type
226  auto result = get_top_entry(env, ns);
227 
228  // Merge each known element into the TOP value
229  for(const auto &element : map.get_view())
230  result =
231  abstract_objectt::merge(result, element.second, widen_modet::no).object;
232 
233  return result;
234 }
235 
237  abstract_environmentt &environment,
238  const namespacet &ns,
239  const std::stack<exprt> &stack,
240  const exprt &expr,
241  const abstract_object_pointert &value,
242  bool merging_write) const
243 {
244  if(is_bottom())
245  {
247  environment, ns, stack, expr, value, merging_write);
248  }
249 
250  if(!stack.empty())
251  return write_sub_element(
252  environment, ns, stack, expr, value, merging_write);
253 
254  return write_leaf_element(environment, ns, expr, value, merging_write);
255 }
256 
258  abstract_environmentt &environment,
259  const namespacet &ns,
260  const std::stack<exprt> &stack,
261  const exprt &expr,
262  const abstract_object_pointert &value,
263  bool merging_write) const
264 {
265  const auto &result =
266  std::dynamic_pointer_cast<full_array_abstract_objectt>(mutable_clone());
267 
268  auto index = eval_index(expr, environment, ns);
269 
270  if(index.is_good)
271  {
272  // We were able to evaluate the index to a value, which we
273  // assume is in bounds...
274  auto const existing_value = map_find_or_top(index.value, environment, ns);
275  result->map_put(
276  index.value,
277  environment.write(existing_value, value, stack, ns, merging_write),
278  index.overrun);
279  result->set_not_top();
280  DATA_INVARIANT(result->verify(), "Structural invariants maintained");
281  return result;
282  }
283  else
284  {
285  // We were not able to evaluate the index to a value
286  for(const auto &starting_value : map.get_view())
287  {
288  // Merging write since we don't know which index we are writing to
289  result->map.replace(
290  starting_value.first,
291  environment.write(starting_value.second, value, stack, ns, true));
292 
293  result->set_not_top();
294  }
295 
296  DATA_INVARIANT(result->verify(), "Structural invariants maintained");
297  return result;
298  }
299 }
300 
302  abstract_environmentt &environment,
303  const namespacet &ns,
304  const exprt &expr,
305  const abstract_object_pointert &value,
306  bool merging_write) const
307 {
308  const auto &result =
309  std::dynamic_pointer_cast<full_array_abstract_objectt>(mutable_clone());
310 
311  auto index = eval_index(expr, environment, ns);
312 
313  if(index.is_good)
314  {
315  // We were able to evaluate the index expression to a constant
316  if(merging_write)
317  {
318  if(is_top())
319  {
320  DATA_INVARIANT(result->verify(), "Structural invariants maintained");
321  return result;
322  }
323 
324  INVARIANT(!result->map.empty(), "If not top, map cannot be empty");
325 
326  auto old_value = result->map.find(index.value);
327  if(old_value.has_value()) // if not found it's top, so nothing to merge
328  {
329  result->map.replace(
330  index.value,
331  abstract_objectt::merge(old_value.value(), value, widen_modet::no)
332  .object);
333  }
334 
335  DATA_INVARIANT(result->verify(), "Structural invariants maintained");
336  return result;
337  }
338  else
339  {
340  result->map_put(index.value, value, index.overrun);
341  result->set_not_top();
342 
343  DATA_INVARIANT(result->verify(), "Structural invariants maintained");
344  return result;
345  }
346  }
347 
348  // try to write to all
349  // TODO(tkiley): Merge with each entry
351  environment, ns, std::stack<exprt>(), expr, value, merging_write);
352 }
353 
355  mp_integer index_value,
356  const abstract_object_pointert &value,
357  bool overrun)
358 {
359  auto old_value = map.find(index_value);
360 
361  if(!old_value.has_value())
362  map.insert(index_value, value);
363  else
364  {
365  // if we're over the max_index, merge with existing value
366  auto replacement_value =
367  overrun
368  ? abstract_objectt::merge(old_value.value(), value, widen_modet::no)
369  .object
370  : value;
371 
372  map.replace(index_value, replacement_value);
373  }
374 }
375 
377  mp_integer index_value,
378  const abstract_environmentt &env,
379  const namespacet &ns) const
380 {
381  auto value = map.find(index_value);
382  if(value.has_value())
383  return value.value();
384  return get_top_entry(env, ns);
385 }
386 
388  const abstract_environmentt &env,
389  const namespacet &ns) const
390 {
391  return env.abstract_object_factory(
392  to_type_with_subtype(type()).subtype(), ns, true, false);
393 }
394 
396  const locationt &location) const
397 {
399 }
400 
402  const locationt &location) const
403 {
405 }
406 
408  const abstract_object_visitort &visitor) const
409 {
410  const auto &result =
411  std::dynamic_pointer_cast<full_array_abstract_objectt>(mutable_clone());
412 
413  bool is_modified = visit_map(result->map, visitor);
414 
415  return is_modified ? result : shared_from_this();
416 }
417 
419  const exprt &name) const
420 {
421  auto all_predicates = exprt::operandst{};
422 
423  for(auto field : map.get_sorted_view())
424  {
425  auto ii = from_integer(field.first.to_long(), integer_typet());
426  auto index = index_exprt(name, ii);
427  auto field_expr = field.second->to_predicate(index);
428 
429  if(!field_expr.is_true())
430  all_predicates.push_back(field_expr);
431  }
432 
433  if(all_predicates.empty())
434  return true_exprt();
435  if(all_predicates.size() == 1)
436  return all_predicates.front();
437  return and_exprt(all_predicates);
438 }
439 
441  abstract_object_statisticst &statistics,
442  abstract_object_visitedt &visited,
443  const abstract_environmentt &env,
444  const namespacet &ns) const
445 {
446  for(const auto &object : map.get_view())
447  {
448  if(visited.find(object.second) == visited.end())
449  {
450  object.second->get_statistics(statistics, visited, env, ns);
451  }
452  }
453  statistics.objects_memory_usage += memory_sizet::from_bytes(sizeof(*this));
454 }
455 
457  const exprt &expr,
458  const abstract_environmentt &env,
459  const namespacet &ns)
460 {
461  const auto &index_expr = to_index_expr(expr);
462  auto index_abstract_object = env.eval(index_expr.index(), ns);
463  auto value = index_abstract_object->to_constant();
464 
465  if(!value.is_constant())
466  return {false};
467 
468  mp_integer out_index;
469  bool result = to_integer(to_constant_expr(value), out_index);
470  if(result)
471  return {false};
472 
473  return eval_index(out_index, env, ns);
474 }
475 
477  const mp_integer &index,
478  const abstract_environmentt &env,
479  const namespacet &ns)
480 {
481  auto max_array_index = env.configuration().maximum_array_index;
482  bool overrun = (index >= max_array_index);
483 
484  return {true, overrun ? max_array_index : index, overrun};
485 }
An abstract version of a program environment.
std::shared_ptr< const T > sharing_ptrt
Merge is designed to allow different abstractions to be merged gracefully.
std::set< abstract_object_pointert > abstract_object_visitedt
sharing_ptrt< class abstract_objectt > abstract_object_pointert
Common behaviour for abstract objects modelling values - constants, intervals, etc.
constant_exprt from_integer(const mp_integer &int_value, const typet &type)
bool to_integer(const constant_exprt &expr, mp_integer &int_value)
Convert a constant expression expr to an arbitrary-precision integer.
Definition: arith_tools.cpp:20
static bool merge_shared_maps(const sharing_mapt< keyt, abstract_object_pointert, false, hash > &map1, const sharing_mapt< keyt, abstract_object_pointert, false, hash > &map2, sharing_mapt< keyt, abstract_object_pointert, false, hash > &out_map, const widen_modet &widen_mode)
virtual abstract_object_pointert write_component(abstract_environmentt &environment, const namespacet &ns, const std::stack< exprt > &stack, const exprt &expr, const abstract_object_pointert &value, bool merging_write) const
virtual abstract_object_pointert eval(const exprt &expr, const namespacet &ns) const
These three are really the heart of the method.
const vsd_configt & configuration() const
Exposes the environment configuration.
virtual abstract_object_pointert write(const abstract_object_pointert &lhs, const abstract_object_pointert &rhs, std::stack< exprt > remaining_stack, const namespacet &ns, bool merge_write)
Used within assign to do the actual dispatch.
virtual abstract_object_pointert abstract_object_factory(const typet &type, const namespacet &ns, bool top, bool bottom) const
Look at the configuration for the sensitivity and create an appropriate abstract_object.
virtual bool is_top() const
Find out if the abstract object is top.
virtual bool verify() const
Verify the internal structure of an abstract_object is correct.
virtual bool is_bottom() const
Find out if the abstract object is bottom.
goto_programt::const_targett locationt
abstract_object_pointert make_top() const
static combine_result merge(const abstract_object_pointert &op1, const abstract_object_pointert &op2, const locationt &merge_location, const widen_modet &widen_mode)
virtual void output(std::ostream &out, const class ai_baset &ai, const namespacet &ns) const
Print the value of the abstract object.
virtual const typet & type() const
Get the real type of the variable this abstract object is representing.
This is the basic interface of the abstract interpreter with default implementations of the core func...
Definition: ai.h:117
Boolean AND.
Definition: std_expr.h:2120
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
abstract_object_pointert merge(const abstract_object_pointert &other, const widen_modet &widen_mode) const override
Tries to do an array/array merge if merging with a constant array If it can't, falls back to parent m...
void map_put(mp_integer index, const abstract_object_pointert &value, bool overrun)
exprt to_predicate_internal(const exprt &name) const override
to_predicate implementation - derived classes will override
void output(std::ostream &out, const ai_baset &ai, const namespacet &ns) const override
the current known value about this object.
abstract_object_pointert get_top_entry(const abstract_environmentt &env, const namespacet &ns) const
Short hand method for creating a top element of the array.
abstract_object_pointert map_find_or_top(mp_integer index, const abstract_environmentt &env, const namespacet &ns) const
full_array_abstract_objectt(typet type, bool top, bool bottom)
Start the abstract object at either top or bottom or neither Asserts if both top and bottom are true.
abstract_object_pointert write_element(abstract_environmentt &environment, const namespacet &ns, const std::stack< exprt > &stack, const exprt &expr, const abstract_object_pointert &value, bool merging_write) const
bool verify() const override
To validate that the struct object is in a valid state.
abstract_object_pointert read_component(const abstract_environmentt &env, const exprt &expr, const namespacet &ns) const override
A helper function to read elements from an array.
abstract_object_pointert merge_location_context(const locationt &location) const override
Update the merge location context for an abstract object.
void set_top_internal() override
Perform any additional structural modifications when setting this object to TOP.
abstract_object_pointert full_array_merge(const full_array_pointert &other, const widen_modet &widen_mode) const
Merges an array into this array.
abstract_object_pointert write_component(abstract_environmentt &environment, const namespacet &ns, const std::stack< exprt > &stack, const exprt &expr, const abstract_object_pointert &value, bool merging_write) const override
A helper function to evaluate writing to a index of an array.
abstract_object_pointert visit_sub_elements(const abstract_object_visitort &visitor) const override
Apply a visitor operation to all sub elements of this abstract_object.
internal_abstract_object_pointert mutable_clone() const override
abstract_object_pointert read_element(const abstract_environmentt &env, const exprt &expr, const namespacet &ns) const
abstract_object_pointert write_leaf_element(abstract_environmentt &environment, const namespacet &ns, const exprt &expr, const abstract_object_pointert &value, bool merging_write) const
abstract_object_pointert write_sub_element(abstract_environmentt &environment, const namespacet &ns, const std::stack< exprt > &stack, const exprt &expr, const abstract_object_pointert &value, bool merging_write) const
abstract_object_pointert write_location_context(const locationt &location) const override
Update the location context for an abstract object.
void statistics(abstract_object_statisticst &statistics, abstract_object_visitedt &visited, const abstract_environmentt &env, const namespacet &ns) const override
sharing_ptrt< full_array_abstract_objectt > const full_array_pointert
Array index operator.
Definition: std_expr.h:1465
exprt & array()
Definition: std_expr.h:1495
exprt & index()
Definition: std_expr.h:1505
Unbounded, signed integers (mathematical integers, not bitvectors)
const irep_idt & id() const
Definition: irep.h:384
static memory_sizet from_bytes(std::size_t bytes)
A namespacet is essentially one or two symbol tables bound together, to allow for symbol lookups in t...
Definition: namespace.h:94
void insert(const key_type &k, valueU &&m)
Insert element, element must not exist in map.
Definition: sharing_map.h:1348
void replace(const key_type &k, valueU &&m)
Replace element, element must exist in map.
Definition: sharing_map.h:1425
bool empty() const
Check if map is empty.
Definition: sharing_map.h:360
sorted_viewt get_sorted_view() const
Convenience function to get a sorted view of the map elements.
Definition: sharing_map.h:462
void clear()
Clear map.
Definition: sharing_map.h:366
std::optional< std::reference_wrapper< const mapped_type > > find(const key_type &k) const
Find element.
Definition: sharing_map.h:1451
void get_view(V &view) const
Get a view of the elements in the map A view is a list of pairs with the components being const refer...
The Boolean constant true.
Definition: std_expr.h:3055
The type of an expression, extends irept.
Definition: type.h:29
abstract_object_pointert apply_to_index_range(const abstract_environmentt &environment, const exprt &expr, const namespacet &ns, index_fn &fn)
static eval_index_resultt eval_index(const exprt &expr, const abstract_environmentt &env, const namespacet &ns)
An abstraction of an array value.
bool visit_map(mapt &map, const visitort &visitor)
Definition: map_visit.h:12
Mathematical types.
BigInt mp_integer
Definition: smt_terms.h:17
#define DATA_INVARIANT(CONDITION, REASON)
This condition should be used to document that assumptions that are made on goto_functions,...
Definition: invariant.h:534
#define PRECONDITION(CONDITION)
Definition: invariant.h:463
API to expression classes.
const constant_exprt & to_constant_expr(const exprt &expr)
Cast an exprt to a constant_exprt.
Definition: std_expr.h:3037
const index_exprt & to_index_expr(const exprt &expr)
Cast an exprt to an index_exprt.
Definition: std_expr.h:1533
Pure virtual interface required of a client that can apply a copy-on-write operation to a given abstr...
abstract_object_pointert object
const type_with_subtypet & to_type_with_subtype(const typet &type)
Definition: type.h:208
Captures the user-supplied configuration for VSD, determining which domain abstractions are used,...