CBMC
ai.cpp
Go to the documentation of this file.
1 /*******************************************************************\
2 
3 Module: Abstract Interpretation
4 
5 Author: Daniel Kroening, kroening@kroening.com
6 
7 \*******************************************************************/
8 
11 
12 #include "ai.h"
13 
14 #include <memory>
15 #include <sstream>
16 #include <type_traits>
17 
18 #include <util/invariant.h>
19 #include <util/std_code.h>
20 
22  const namespacet &ns,
23  const goto_functionst &goto_functions,
24  std::ostream &out) const
25 {
26  for(const auto &gf_entry : goto_functions.function_map)
27  {
28  if(gf_entry.second.body_available())
29  {
30  out << "////\n";
31  out << "//// Function: " << gf_entry.first << "\n";
32  out << "////\n";
33  out << "\n";
34 
35  output(ns, gf_entry.first, gf_entry.second.body, out);
36  }
37  }
38 }
39 
41  const namespacet &ns,
42  const irep_idt &function_id,
43  const goto_programt &goto_program,
44  std::ostream &out) const
45 {
46  (void)function_id; // unused parameter
47 
48  forall_goto_program_instructions(i_it, goto_program)
49  {
50  out << "**** " << i_it->location_number << " " << i_it->source_location()
51  << "\n";
52 
53  abstract_state_before(i_it)->output(out, *this, ns);
54  out << "\n";
55  #if 1
56  i_it->output(out);
57  out << "\n";
58  #endif
59  }
60 }
61 
63  const namespacet &ns,
64  const goto_functionst &goto_functions) const
65 {
66  json_objectt result;
67 
68  for(const auto &gf_entry : goto_functions.function_map)
69  {
70  if(gf_entry.second.body_available())
71  {
72  result[id2string(gf_entry.first)] =
73  output_json(ns, gf_entry.first, gf_entry.second.body);
74  }
75  else
76  {
77  result[id2string(gf_entry.first)] = json_arrayt();
78  }
79  }
80 
81  return std::move(result);
82 }
83 
85  const namespacet &ns,
86  const irep_idt &function_id,
87  const goto_programt &goto_program) const
88 {
89  (void)function_id; // unused parameter
90 
91  json_arrayt contents;
92 
93  forall_goto_program_instructions(i_it, goto_program)
94  {
95  // Ideally we need output_instruction_json
96  std::ostringstream out;
97  i_it->output(out);
98 
99  json_objectt location{
100  {"locationNumber", json_numbert(std::to_string(i_it->location_number))},
101  {"sourceLocation", json_stringt(i_it->source_location().as_string())},
102  {"abstractState", abstract_state_before(i_it)->output_json(*this, ns)},
103  {"instruction", json_stringt(out.str())}};
104 
105  contents.push_back(std::move(location));
106  }
107 
108  return std::move(contents);
109 }
110 
112  const namespacet &ns,
113  const goto_functionst &goto_functions) const
114 {
115  xmlt program("program");
116 
117  for(const auto &gf_entry : goto_functions.function_map)
118  {
119  xmlt function(
120  "function",
121  {{"name", id2string(gf_entry.first)},
122  {"body_available", gf_entry.second.body_available() ? "true" : "false"}},
123  {});
124 
125  if(gf_entry.second.body_available())
126  {
127  function.new_element(
128  output_xml(ns, gf_entry.first, gf_entry.second.body));
129  }
130 
131  program.new_element(function);
132  }
133 
134  return program;
135 }
136 
138  const namespacet &ns,
139  const irep_idt &function_id,
140  const goto_programt &goto_program) const
141 {
142  (void)function_id; // unused parameter
143 
144  xmlt function_body;
145 
146  forall_goto_program_instructions(i_it, goto_program)
147  {
148  xmlt location(
149  "",
150  {{"location_number", std::to_string(i_it->location_number)},
151  {"source_location", i_it->source_location().as_string()}},
152  {abstract_state_before(i_it)->output_xml(*this, ns)});
153 
154  // Ideally we need output_instruction_xml
155  std::ostringstream out;
156  i_it->output(out);
157  location.set_attribute("instruction", out.str());
158 
159  function_body.new_element(location);
160  }
161 
162  return function_body;
163 }
164 
167 {
168  // find the 'entry function'
169 
170  goto_functionst::function_mapt::const_iterator
171  f_it=goto_functions.function_map.find(goto_functions.entry_point());
172 
173  if(f_it!=goto_functions.function_map.end())
174  return entry_state(f_it->second.body);
175 
176  // It is not clear if this is even a well-structured goto_functionst object
177  return nullptr;
178 }
179 
181 {
182  // The first instruction of 'goto_program' is the entry point
183  trace_ptrt p = history_factory->epoch(goto_program.instructions.begin());
184  get_state(p).make_entry();
185  return p;
186 }
187 
189  const irep_idt &function_id,
190  const goto_functionst::goto_functiont &goto_function)
191 {
192  initialize(function_id, goto_function.body);
193 }
194 
195 void ai_baset::initialize(const irep_idt &, const goto_programt &goto_program)
196 {
197  // Domains are created and set to bottom on access.
198  // So we do not need to set them to be bottom before hand.
199 }
200 
201 void ai_baset::initialize(const goto_functionst &goto_functions)
202 {
203  for(const auto &gf_entry : goto_functions.function_map)
204  initialize(gf_entry.first, gf_entry.second);
205 }
206 
208 {
209  // Nothing to do per default
210 }
211 
213 {
214  PRECONDITION(!working_set.empty());
215 
216  static_assert(
217  std::is_same<
218  working_sett,
219  std::set<trace_ptrt, ai_history_baset::compare_historyt>>::value,
220  "begin must return the minimal entry");
221  auto first = working_set.begin();
222 
223  trace_ptrt t = *first;
224 
225  working_set.erase(first);
226 
227  return t;
228 }
229 
231  trace_ptrt start_trace,
232  const irep_idt &function_id,
233  const goto_programt &goto_program,
234  const goto_functionst &goto_functions,
235  const namespacet &ns)
236 {
237  PRECONDITION(start_trace != nullptr);
238 
239  working_sett working_set;
240  put_in_working_set(working_set, start_trace);
241 
242  bool new_data=false;
243 
244  while(!working_set.empty())
245  {
246  trace_ptrt p = get_next(working_set);
247 
248  // goto_program is really only needed for iterator manipulation
249  if(visit(function_id, p, working_set, goto_program, goto_functions, ns))
250  new_data=true;
251  }
252 
253  return new_data;
254 }
255 
257  trace_ptrt start_trace,
258  const goto_functionst &goto_functions,
259  const namespacet &ns)
260 {
261  goto_functionst::function_mapt::const_iterator f_it =
262  goto_functions.function_map.find(goto_functions.entry_point());
263 
264  if(f_it != goto_functions.function_map.end())
265  fixedpoint(start_trace, f_it->first, f_it->second.body, goto_functions, ns);
266 }
267 
269  const irep_idt &function_id,
270  trace_ptrt p,
271  working_sett &working_set,
272  const goto_programt &goto_program,
273  const goto_functionst &goto_functions,
274  const namespacet &ns)
275 {
276  bool new_data=false;
277  locationt l = p->current_location();
279 
280  log.progress() << "ai_baset::visit " << l->location_number << " in "
281  << function_id << messaget::eom;
282 
283  // Function call and end are special cases
284  if(l->is_function_call())
285  {
287  goto_program.get_successors(l).size() == 1,
288  "function calls only have one successor");
289 
291  *(goto_program.get_successors(l).begin()) == std::next(l),
292  "function call successor / return location must be the next instruction");
293 
294  new_data = visit_function_call(
295  function_id, p, working_set, goto_program, goto_functions, ns);
296  }
297  else if(l->is_end_function())
298  {
300  goto_program.get_successors(l).empty(),
301  "The end function instruction should have no successors.");
302 
303  new_data = visit_end_function(
304  function_id, p, working_set, goto_program, goto_functions, ns);
305  }
306  else
307  {
308  // Successors can be empty, for example assume(0).
309  // Successors can contain duplicates, for example GOTO next;
310  for(const auto &to_l : goto_program.get_successors(l))
311  {
312  if(to_l == goto_program.instructions.end())
313  continue;
314 
315  new_data |= visit_edge(
316  function_id,
317  p,
318  function_id,
319  to_l,
321  ns,
322  working_set); // Local steps so no caller history needed
323  }
324  }
325 
326  return new_data;
327 }
328 
330  const irep_idt &function_id,
331  trace_ptrt p,
332  const irep_idt &to_function_id,
333  locationt to_l,
334  trace_ptrt caller_history,
335  const namespacet &ns,
336  working_sett &working_set)
337 {
339  log.progress() << "ai_baset::visit_edge from "
340  << p->current_location()->location_number << " to "
341  << to_l->location_number << "... ";
342 
343  // Has history taught us not to step here...
344  auto next =
345  p->step(to_l, *(storage->abstract_traces_before(to_l)), caller_history);
347  {
348  log.progress() << "blocked by history" << messaget::eom;
349  return false;
350  }
351  else if(next.first == ai_history_baset::step_statust::NEW)
352  {
353  log.progress() << "gives a new history... ";
354  }
355  else
356  {
357  log.progress() << "merges with existing history... ";
358  }
359  trace_ptrt to_p = next.second;
360 
361  // Abstract domains are mutable so we must copy before we transform
362  statet &current = get_state(p);
363 
364  std::unique_ptr<statet> tmp_state(make_temporary_state(current));
365  statet &new_values = *tmp_state;
366 
367  // Apply transformer
368  log.progress() << "applying transformer... ";
369  new_values.transform(function_id, p, to_function_id, to_p, *this, ns);
370 
371  // Expanding a domain means that it has to be analysed again
372  // Likewise if the history insists that it is a new trace
373  // (assuming it is actually reachable).
374  log.progress() << "merging... ";
375  bool return_value = false;
376  if(
377  merge(new_values, p, to_p) ||
378  (next.first == ai_history_baset::step_statust::NEW &&
379  !new_values.is_bottom()))
380  {
381  put_in_working_set(working_set, to_p);
382  log.progress() << "result queued." << messaget::eom;
383  return_value = true;
384  }
385  else
386  {
387  log.progress() << "domain unchanged." << messaget::eom;
388  }
389 
390  // Branch because printing some histories and domains can be expensive
391  // esp. if the output is then just discarded and this is a critical path.
392  if(message_handler.get_verbosity() >= messaget::message_levelt::M_DEBUG)
393  {
394  log.debug() << "p = ";
395  p->output(log.debug());
396  log.debug() << messaget::eom;
397 
398  log.debug() << "current = ";
399  current.output(log.debug(), *this, ns);
400  log.debug() << messaget::eom;
401 
402  log.debug() << "to_p = ";
403  to_p->output(log.debug());
404  log.debug() << messaget::eom;
405 
406  log.debug() << "new_values = ";
407  new_values.output(log.debug(), *this, ns);
408  log.debug() << messaget::eom;
409  }
410 
411  return return_value;
412 }
413 
415  const irep_idt &calling_function_id,
416  trace_ptrt p_call,
417  locationt l_return,
418  const irep_idt &,
419  working_sett &working_set,
420  const goto_programt &,
421  const goto_functionst &,
422  const namespacet &ns)
423 {
425  log.progress() << "ai_baset::visit_edge_function_call from "
426  << p_call->current_location()->location_number << " to "
427  << l_return->location_number << messaget::eom;
428 
429  // The default implementation is not interprocedural
430  // so the effects of the call are approximated but nothing else
431  return visit_edge(
432  calling_function_id,
433  p_call,
434  calling_function_id,
435  l_return,
437  no_caller_history, // Not needed as we are skipping the function call
438  ns,
439  working_set);
440 }
441 
443  const irep_idt &calling_function_id,
444  trace_ptrt p_call,
445  working_sett &working_set,
446  const goto_programt &caller,
447  const goto_functionst &goto_functions,
448  const namespacet &ns)
449 {
450  locationt l_call = p_call->current_location();
451 
452  PRECONDITION(l_call->is_function_call());
453 
455  log.progress() << "ai_baset::visit_function_call at "
456  << l_call->location_number << messaget::eom;
457 
458  locationt l_return = std::next(l_call);
459 
460  // operator() allows analysis of a single goto_program independently
461  // it generates a synthetic goto_functions object for this
462  if(!goto_functions.function_map.empty())
463  {
464  const exprt &callee_expression = l_call->call_function();
465 
466  if(callee_expression.id() == ID_symbol)
467  {
468  const irep_idt &callee_function_id =
469  to_symbol_expr(callee_expression).get_identifier();
470 
471  log.progress() << "Calling " << callee_function_id << messaget::eom;
472 
473  goto_functionst::function_mapt::const_iterator it =
474  goto_functions.function_map.find(callee_function_id);
475 
477  it != goto_functions.function_map.end(),
478  "Function " + id2string(callee_function_id) + "not in function map");
479 
480  const goto_functionst::goto_functiont &callee_fun = it->second;
481 
482  if(callee_fun.body_available())
483  {
485  calling_function_id,
486  p_call,
487  l_return,
488  callee_function_id,
489  working_set,
490  callee_fun.body,
491  goto_functions,
492  ns);
493  }
494  else
495  {
496  // Fall through to the default, body not available, case
497  }
498  }
499  else
500  {
501  // Function pointers are not currently supported and must be removed
503  callee_expression.id() == ID_symbol,
504  "Function pointers and indirect calls must be removed before "
505  "analysis.");
506  }
507  }
508 
509  // If the body is not available then we just do the edge from call to return
510  // in the caller. Domains should over-approximate what the function might do.
511  return visit_edge(
512  calling_function_id,
513  p_call,
514  calling_function_id,
515  l_return,
516  ai_history_baset::no_caller_history, // Would be the same as p_call...
517  ns,
518  working_set);
519 }
520 
522  const irep_idt &function_id,
523  trace_ptrt p,
524  working_sett &working_set,
525  const goto_programt &goto_program,
526  const goto_functionst &goto_functions,
527  const namespacet &ns)
528 {
529  locationt l = p->current_location();
530  PRECONDITION(l->is_end_function());
531 
533  log.progress() << "ai_baset::visit_end_function " << function_id
534  << messaget::eom;
535 
536  // Do nothing
537  return false;
538 }
539 
541  const irep_idt &calling_function_id,
542  trace_ptrt p_call,
543  locationt l_return,
544  const irep_idt &callee_function_id,
545  working_sett &working_set,
546  const goto_programt &callee,
547  const goto_functionst &goto_functions,
548  const namespacet &ns)
549 {
551  log.progress() << "ai_recursive_interproceduralt::visit_edge_function_call"
552  << " from " << p_call->current_location()->location_number
553  << " to " << l_return->location_number << messaget::eom;
554 
555  // This is the edge from call site to function head.
556  {
557  locationt l_begin = callee.instructions.begin();
558 
559  working_sett catch_working_set; // The trace from the next fixpoint
560 
561  // Do the edge from the call site to the beginning of the function
562  bool new_data = visit_edge(
563  calling_function_id,
564  p_call,
565  callee_function_id,
566  l_begin,
568  no_caller_history, // Not needed as p_call already has the info
569  ns,
570  catch_working_set);
571 
572  log.progress() << "Handle " << callee_function_id << " recursively"
573  << messaget::eom;
574 
575  // do we need to do/re-do the fixedpoint of the body?
576  if(new_data)
577  fixedpoint(
578  get_next(catch_working_set),
579  callee_function_id,
580  callee,
581  goto_functions,
582  ns);
583  }
584 
585  // This is the edge from function end to return site.
586  {
587  log.progress() << "Handle return edges" << messaget::eom;
588 
589  // get location at end of the procedure we have called
590  locationt l_end = --callee.instructions.end();
592  l_end->is_end_function(),
593  "The last instruction of a goto_program must be END_FUNCTION");
594 
595  // Find the histories for a location
596  auto traces = storage->abstract_traces_before(l_end);
597 
598  bool new_data = false;
599 
600  // The history used may mean there are multiple histories at the end of the
601  // function. Some of these can be progressed (for example, if the history
602  // tracks paths within functions), some can't be (for example, not all
603  // calling contexts will be appropriate). We rely on the history's step,
604  // called from visit_edge to prune as applicable.
605  for(auto p_end : *traces)
606  {
607  // do edge from end of function to instruction after call
608  const statet &end_state = get_state(p_end);
609 
610  if(!end_state.is_bottom())
611  {
612  // function exit point reachable in that history
613 
614  new_data |= visit_edge(
615  callee_function_id,
616  p_end,
617  calling_function_id,
618  l_return,
619  p_call, // To allow function-local history
620  ns,
621  working_set);
622  }
623  }
624 
625  return new_data;
626  }
627 }
messaget
Class that provides messages with a built-in verbosity 'level'.
Definition: message.h:154
dstringt
dstringt has one field, an unsigned integer no which is an index into a static table of strings.
Definition: dstring.h:36
ai_baset::output_json
virtual jsont output_json(const namespacet &ns, const goto_functionst &goto_functions) const
Output the abstract states for the whole program as JSON.
Definition: ai.cpp:62
json_numbert
Definition: json.h:290
ai_baset::get_next
trace_ptrt get_next(working_sett &working_set)
Get the next location from the work queue.
Definition: ai.cpp:212
ai_baset::visit_end_function
virtual bool visit_end_function(const irep_idt &function_id, trace_ptrt p, working_sett &working_set, const goto_programt &goto_program, const goto_functionst &goto_functions, const namespacet &ns)
Definition: ai.cpp:521
ai_baset::locationt
goto_programt::const_targett locationt
Definition: ai.h:126
ai_baset::working_sett
trace_sett working_sett
The work queue, sorted using the history's ordering operator.
Definition: ai.h:416
ai_baset::visit_function_call
virtual bool visit_function_call(const irep_idt &function_id, trace_ptrt p_call, working_sett &working_set, const goto_programt &goto_program, const goto_functionst &goto_functions, const namespacet &ns)
Definition: ai.cpp:442
message_handlert::get_verbosity
unsigned get_verbosity() const
Definition: message.h:54
ai_history_baset::step_statust::BLOCKED
@ BLOCKED
ai_baset::make_temporary_state
virtual std::unique_ptr< statet > make_temporary_state(const statet &s)
Make a copy of a state.
Definition: ai.h:507
ai_baset::message_handler
message_handlert & message_handler
Definition: ai.h:523
ai_baset::visit
virtual bool visit(const irep_idt &function_id, trace_ptrt p, working_sett &working_set, const goto_programt &goto_program, const goto_functionst &goto_functions, const namespacet &ns)
Perform one step of abstract interpretation from trace t Depending on the instruction type it may com...
Definition: ai.cpp:268
ai_baset::put_in_working_set
void put_in_working_set(working_sett &working_set, trace_ptrt t)
Definition: ai.h:421
ai_baset::output_xml
virtual xmlt output_xml(const namespacet &ns, const goto_functionst &goto_functions) const
Output the abstract states for the whole program as XML.
Definition: ai.cpp:111
invariant.h
exprt
Base class for all expressions.
Definition: expr.h:55
ai_baset::abstract_state_before
virtual cstate_ptrt abstract_state_before(locationt l) const
Get a copy of the abstract state before the given instruction, without needing to know what kind of d...
Definition: ai.h:223
to_string
std::string to_string(const string_not_contains_constraintt &expr)
Used for debug printing.
Definition: string_constraint.cpp:58
ai_history_baset::no_caller_history
static const trace_ptrt no_caller_history
Definition: ai_history.h:121
messaget::eom
static eomt eom
Definition: message.h:297
goto_programt::get_successors
std::list< Target > get_successors(Target target) const
Get control-flow successors of a given instruction.
Definition: goto_program.h:1117
goto_functionst::function_map
function_mapt function_map
Definition: goto_functions.h:29
jsont
Definition: json.h:26
ai_domain_baset::is_bottom
virtual bool is_bottom() const =0
json_arrayt
Definition: json.h:164
json_objectt
Definition: json.h:299
xmlt::output
void output(std::ostream &out, unsigned indent=0) const
Definition: xml.cpp:33
namespacet
A namespacet is essentially one or two symbol tables bound together, to allow for symbol lookups in t...
Definition: namespace.h:90
ai_baset::get_state
virtual statet & get_state(trace_ptrt p)
Get the state for the given history, creating it with the factory if it doesn't exist.
Definition: ai.h:517
ai_domain_baset::transform
virtual void transform(const irep_idt &function_from, trace_ptrt from, const irep_idt &function_to, trace_ptrt to, ai_baset &ai, const namespacet &ns)=0
how function calls are treated: a) there is an edge from each call site to the function head b) there...
DATA_INVARIANT
#define DATA_INVARIANT(CONDITION, REASON)
This condition should be used to document that assumptions that are made on goto_functions,...
Definition: invariant.h:510
id2string
const std::string & id2string(const irep_idt &d)
Definition: irep.h:47
PRECONDITION
#define PRECONDITION(CONDITION)
Definition: invariant.h:463
symbol_exprt::get_identifier
const irep_idt & get_identifier() const
Definition: std_expr.h:142
ai_baset::storage
std::unique_ptr< ai_storage_baset > storage
Definition: ai.h:513
to_symbol_expr
const symbol_exprt & to_symbol_expr(const exprt &expr)
Cast an exprt to a symbol_exprt.
Definition: std_expr.h:222
ai_baset::history_factory
std::unique_ptr< ai_history_factory_baset > history_factory
For creating history objects.
Definition: ai.h:493
irept::id
const irep_idt & id() const
Definition: irep.h:396
ai.h
std_code.h
ai_baset::finalize
virtual void finalize()
Override this to add a cleanup or post-processing step after fixedpoint has run.
Definition: ai.cpp:207
xmlt
Definition: xml.h:20
ai_baset::visit_edge
bool visit_edge(const irep_idt &function_id, trace_ptrt p, const irep_idt &to_function_id, locationt to_l, trace_ptrt caller_history, const namespacet &ns, working_sett &working_set)
Definition: ai.cpp:329
ai_baset::entry_state
trace_ptrt entry_state(const goto_programt &goto_program)
Set the abstract state of the entry location of a single function to the entry state required by the ...
Definition: ai.cpp:180
ai_baset::merge
virtual bool merge(const statet &src, trace_ptrt from, trace_ptrt to)
Merge the state src, flowing from tracet from to tracet to, into the state currently stored for trace...
Definition: ai.h:500
goto_functionst::goto_functiont
::goto_functiont goto_functiont
Definition: goto_functions.h:27
goto_programt::instructions
instructionst instructions
The list of instructions in the goto program.
Definition: goto_program.h:592
ai_baset::initialize
virtual void initialize(const irep_idt &function_id, const goto_programt &goto_program)
Initialize all the abstract states for a single function.
Definition: ai.cpp:195
goto_functionst
A collection of goto functions.
Definition: goto_functions.h:24
ai_baset::fixedpoint
virtual bool fixedpoint(trace_ptrt starting_trace, const irep_idt &function_id, const goto_programt &goto_program, const goto_functionst &goto_functions, const namespacet &ns)
Run the fixedpoint algorithm until it reaches a fixed point.
Definition: ai.cpp:230
ai_history_baset
A history object is an abstraction / representation of the control-flow part of a set of traces.
Definition: ai_history.h:36
ai_history_baset::step_statust::NEW
@ NEW
ai_baset::output
virtual void output(const namespacet &ns, const irep_idt &function_id, const goto_programt &goto_program, std::ostream &out) const
Output the abstract states for a single function.
Definition: ai.cpp:40
goto_programt
A generic container class for the GOTO intermediate representation of one function.
Definition: goto_program.h:72
ai_baset::visit_edge_function_call
virtual bool visit_edge_function_call(const irep_idt &calling_function_id, trace_ptrt p_call, locationt l_return, const irep_idt &callee_function_id, working_sett &working_set, const goto_programt &callee, const goto_functionst &goto_functions, const namespacet &ns)
Definition: ai.cpp:414
ai_baset::trace_ptrt
ai_history_baset::trace_ptrt trace_ptrt
Definition: ai.h:123
ai_domain_baset::make_entry
virtual void make_entry()=0
Make this domain a reasonable entry-point state.
messaget::debug
mstreamt & debug() const
Definition: message.h:429
goto_functionst::entry_point
static irep_idt entry_point()
Get the identifier of the entry point to a goto model.
Definition: goto_functions.h:92
messaget::progress
mstreamt & progress() const
Definition: message.h:424
ai_domain_baset
The interface offered by a domain, allows code to manipulate domains without knowing their exact type...
Definition: ai_domain.h:54
ai_domain_baset::output
virtual void output(std::ostream &, const ai_baset &, const namespacet &) const
Definition: ai_domain.h:105
json_arrayt::push_back
jsont & push_back(const jsont &json)
Definition: json.h:212
forall_goto_program_instructions
#define forall_goto_program_instructions(it, program)
Definition: goto_program.h:1229
xmlt::new_element
xmlt & new_element(const std::string &key)
Definition: xml.h:95
ai_recursive_interproceduralt::visit_edge_function_call
bool visit_edge_function_call(const irep_idt &calling_function_id, trace_ptrt p_call, locationt l_return, const irep_idt &callee_function_id, working_sett &working_set, const goto_programt &callee, const goto_functionst &goto_functions, const namespacet &ns) override
Definition: ai.cpp:540
json_stringt
Definition: json.h:269