CBMC
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
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
20#include "map_visit.h"
21
28
30 const exprt &expr,
32 const namespacet &ns);
34 const mp_integer &index,
36 const namespacet &ns);
37
38template <typename index_fn>
40 const abstract_environmentt &environment,
41 const exprt &expr,
42 const namespacet &ns,
43 index_fn &fn)
44{
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
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 }
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)
119
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
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
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
373 }
374}
375
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
389 const namespacet &ns) const
390{
391 return env.abstract_object_factory(
392 to_type_with_subtype(type()).subtype(), ns, true, false);
393}
394
400
406
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{
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();
438}
439
441 abstract_object_statisticst &statistics,
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,
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
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,
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::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.
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.
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
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
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:1470
Unbounded, signed integers (mathematical integers, not bitvectors)
const irep_idt & id() const
Definition irep.h:388
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:91
void insert(const key_type &k, valueU &&m)
Insert element, element must not exist in map.
void replace(const key_type &k, valueU &&m)
Replace element, element must exist in map.
bool empty() const
Check if map is empty.
sorted_viewt get_sorted_view() const
Convenience function to get a sorted view of the map elements.
void clear()
Clear map.
std::optional< std::reference_wrapper< const mapped_type > > find(const key_type &k) const
Find element.
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:3190
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
#define INVARIANT(CONDITION, REASON)
This macro uses the wrapper function 'invariant_violated_string'.
Definition invariant.h:423
API to expression classes.
const index_exprt & to_index_expr(const exprt &expr)
Cast an exprt to an index_exprt.
Definition std_expr.h:1538
const constant_exprt & to_constant_expr(const exprt &expr)
Cast an exprt to a constant_exprt.
Definition std_expr.h:3172
Pure virtual interface required of a client that can apply a copy-on-write operation to a given abstr...
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,...