CBMC
inductiveness.cpp
Go to the documentation of this file.
1 /*******************************************************************\
2 
3 Module: Inductiveness
4 
5 Author: Daniel Kroening, dkr@amazon.com
6 
7 \*******************************************************************/
8 
11 
12 #include "inductiveness.h"
13 
14 #include <util/console.h>
15 #include <util/cout_message.h>
16 #include <util/format_expr.h>
17 
18 #include <solvers/sat/satcheck.h>
19 
20 #include "axioms.h"
21 #include "bv_pointers_wide.h"
22 #include "counterexample_found.h"
23 #include "propagate.h"
24 #include "solver.h"
25 
26 #include <algorithm>
27 #include <iomanip>
28 #include <iostream>
29 
31  const std::unordered_set<exprt, irep_hash> &a1,
32  const std::unordered_set<exprt, irep_hash> &a2,
33  const std::unordered_set<exprt, irep_hash> &a3,
34  const exprt &b,
35  const std::unordered_set<symbol_exprt, irep_hash> &address_taken,
36  bool verbose,
37  const namespacet &ns)
38 {
39  if(b.is_true())
40  return true; // anything subsumes 'true'
41 
42  if(a1.find(false_exprt()) != a1.end())
43  return true; // 'false' subsumes anything
44 
45  if(a1.find(b) != a1.end())
46  return true; // b is subsumed by a conjunct in a
47 
48  cout_message_handlert message_handler;
49 #if 0
50  message_handler.set_verbosity(verbose ? 10 : 1);
51 #else
52  message_handler.set_verbosity(1);
53 #endif
54  satcheckt satcheck(message_handler);
55  bv_pointers_widet solver(ns, satcheck, message_handler);
56  axiomst axioms(solver, address_taken, verbose, ns);
57 
58  // check if a => b is valid,
59  // or (!a || b) is valid,
60  // or (a && !b) is unsat
61  for(auto &a_conjunct : a1)
62  axioms << a_conjunct;
63 
64  for(auto &a_conjunct : a2)
65  axioms << a_conjunct;
66 
67  for(auto &a_conjunct : a3)
68  axioms << a_conjunct;
69 
70  axioms.set_to_false(b);
71 
72  // instantiate our axioms
73  axioms.emit();
74 
75  // now run solver
76  switch(solver())
77  {
79  if(verbose)
81  return false;
83  return true;
85  throw "error reported by solver";
86  }
87 
88  UNREACHABLE; // to silence a warning
89 }
90 
91 std::size_t count_frame(const workt::patht &path, frame_reft f)
92 {
93  return std::count_if(path.begin(), path.end(), [f](const frame_reft &frame) {
94  return f == frame;
95  });
96 }
97 
99  std::vector<framet> &frames,
100  const std::unordered_set<symbol_exprt, irep_hash> &address_taken,
101  const solver_optionst &solver_options,
102  const namespacet &ns,
103  std::vector<propertyt> &properties,
104  std::size_t property_index)
105 {
106  const auto frame_map = build_frame_map(frames);
107  auto &property = properties[property_index];
108 
109  // add properties proven so far as auxiliaries
110  for(std::size_t i = 0; i < property_index; i++)
111  {
112  const auto &p = properties[i];
113  if(p.status == propertyt::PASS)
114  frames[p.frame.index].add_auxiliary(p.condition);
115  }
116 
117  std::vector<workt> queue;
118  std::vector<workt> dropped;
119 
120  auto propagator =
121  [&frames,
122  &frame_map,
123  &queue,
124  &dropped,
125  &address_taken,
126  &solver_options,
127  &ns](
128  const symbol_exprt &symbol, exprt invariant, const workt::patht &path) {
129  auto frame_ref = find_frame(frame_map, symbol);
130  auto &f = frames[frame_ref.index];
131 
132 #if 0
133  if(solver_options.verbose)
134  {
135  std::cout << "F: " << format(symbol) << " <- " << format(invariant)
136  << '\n';
137  }
138 #endif
139 
140  if(solver_options.verbose)
141  {
142  // print the current invariants and obligations in the frame
143  for(const auto &invariant : f.invariants)
144  {
145  std::cout << consolet::faint << consolet::blue;
146  std::cout << 'I' << std::setw(2) << frame_ref.index << ' ';
147  std::cout << format(invariant);
148  std::cout << consolet::reset << '\n';
149  }
150  for(const auto &obligation : f.obligations)
151  {
152  std::cout << consolet::faint << consolet::blue;
153  std::cout << 'O' << std::setw(2) << frame_ref.index << ' ';
154  std::cout << format(obligation);
155  std::cout << consolet::reset << '\n';
156  }
157  }
158 
159  if(solver_options.verbose)
160  std::cout << "\u2192" << consolet::faint << std::setw(2)
161  << frame_ref.index << consolet::reset << ' ';
162 
163  // trivially true?
164  if(invariant.is_true())
165  {
166  if(solver_options.verbose)
167  std::cout << "trivial\n";
168  }
169  else if(is_subsumed(
170  f.invariants_set,
171  f.obligations_set,
172  f.auxiliaries_set,
173  invariant,
175  solver_options.verbose,
176  ns))
177  {
178  if(solver_options.verbose)
179  std::cout << "subsumed " << format(invariant) << '\n';
180  }
181  else if(count_frame(path, frame_ref) > solver_options.loop_limit)
182  {
183  // loop limit exceeded, drop it
184  if(solver_options.verbose)
185  std::cout << consolet::red << "dropped" << consolet::reset << ' '
186  << format(invariant) << '\n';
187  dropped.emplace_back(frame_ref, invariant, path);
188  }
189  else
190  {
191  // propagate
192  if(solver_options.verbose)
193  std::cout << format(invariant) << '\n';
194 
195  // store in frame
196  frames[frame_ref.index].add_obligation(invariant);
197 
198  // add to queue
199  auto new_path = path;
200  new_path.push_back(frame_ref);
201  queue.emplace_back(f.ref, std::move(invariant), std::move(new_path));
202  }
203  };
204 
205  // stick invariants into the queue
206  for(std::size_t frame_index = 0; frame_index < frames.size(); frame_index++)
207  {
208  frame_reft frame_ref(frame_index);
209  for(auto &cond : frames[frame_index].invariants)
210  queue.emplace_back(frame_ref, cond, workt::patht{frame_ref});
211  }
212 
213  // clean up the obligations
214  for(auto &frame : frames)
215  {
216  frame.obligations.clear();
217  frame.obligations_set.clear();
218  }
219 
220  while(!queue.empty())
221  {
222  auto work = queue.back();
223  queue.pop_back();
224 
225 #if 0
226  if(solver_options.verbose)
227  {
228  dump(frames, property, true, true);
229  std::cout << '\n';
230  }
231 #endif
232 
234  frames, work, address_taken, solver_options.verbose, ns);
235 
237  {
238  property.trace = counterexample_found.value();
239  return inductiveness_resultt::base_case_fail(std::move(work));
240  }
241 
242  propagate(
243  frames, work, address_taken, solver_options.verbose, ns, propagator);
244 
245  // did we drop anything?
246  if(!dropped.empty())
247  return inductiveness_resultt::step_case_fail(std::move(dropped.front()));
248  }
249 
250  // done, saturated
252 }
std::unordered_set< symbol_exprt, irep_hash > address_taken(const std::vector< exprt > &src)
Axioms.
Definition: axioms.h:29
void emit()
Definition: axioms.cpp:749
void set_to_false(exprt)
Definition: axioms.cpp:34
static std::ostream & blue(std::ostream &)
Definition: console.cpp:104
static std::ostream & reset(std::ostream &)
Definition: console.cpp:176
static std::ostream & faint(std::ostream &)
Definition: console.cpp:160
static std::ostream & red(std::ostream &)
Definition: console.cpp:128
Base class for all expressions.
Definition: expr.h:56
bool is_true() const
Return whether the expression is a constant representing true.
Definition: expr.cpp:27
The Boolean constant false.
Definition: std_expr.h:3072
static inductiveness_resultt inductive()
Definition: inductiveness.h:29
static inductiveness_resultt step_case_fail(workt dropped)
Definition: inductiveness.h:41
static inductiveness_resultt base_case_fail(workt refuted)
Definition: inductiveness.h:34
void set_verbosity(unsigned _verbosity)
Definition: message.h:52
A namespacet is essentially one or two symbol tables bound together, to allow for symbol lookups in t...
Definition: namespace.h:94
bool verbose
Definition: solver.h:31
std::size_t loop_limit
Definition: solver.h:32
Expression to hold a symbol (variable)
Definition: std_expr.h:131
Console.
std::optional< propertyt::tracet > counterexample_found(const std::vector< framet > &frames, const workt &work, const std::unordered_set< symbol_exprt, irep_hash > &address_taken, bool verbose, const namespacet &ns)
void show_assignment(const bv_pointers_widet &solver)
Counterexample Found.
static format_containert< T > format(const T &o)
Definition: format.h:37
inductiveness_resultt inductiveness_check(std::vector< framet > &frames, const std::unordered_set< symbol_exprt, irep_hash > &address_taken, const solver_optionst &solver_options, const namespacet &ns, std::vector< propertyt > &properties, std::size_t property_index)
std::size_t count_frame(const workt::patht &path, frame_reft f)
bool is_subsumed(const std::unordered_set< exprt, irep_hash > &a1, const std::unordered_set< exprt, irep_hash > &a2, const std::unordered_set< exprt, irep_hash > &a3, const exprt &b, const std::unordered_set< symbol_exprt, irep_hash > &address_taken, bool verbose, const namespacet &ns)
Inductiveness.
void propagate(const std::vector< framet > &frames, const workt &work, const std::unordered_set< symbol_exprt, irep_hash > &address_taken, bool verbose, const namespacet &ns, const std::function< void(const symbol_exprt &, exprt, const workt::patht &)> &propagator)
Definition: propagate.cpp:24
Propagate.
void solver(std::vector< framet > &frames, const std::unordered_set< symbol_exprt, irep_hash > &address_taken, const solver_optionst &solver_options, const namespacet &ns, std::vector< propertyt > &properties, std::size_t property_index)
Definition: solver.cpp:44
Equality Propagation.
frame_reft find_frame(const frame_mapt &frame_map, const symbol_exprt &frame_symbol)
frame_mapt build_frame_map(const std::vector< framet > &frames)
void dump(const std::vector< framet > &frames, const propertyt &property, bool values, bool implications)
#define UNREACHABLE
This should be used to mark dead code.
Definition: invariant.h:525
std::vector< frame_reft > patht
Definition: solver_types.h:167